ETH Price: $2,102.18 (+3.29%)

Transaction Decoder

Block:
15893311 at Nov-04-2022 01:16:59 AM +UTC
Transaction Fee:
0.00453963722923232 ETH $9.54
Gas Used:
332,704 Gas / 13.64467283 Gwei

Emitted Events:

235 FiatTokenProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000f8f92b7af05d01f76aa84d3fbf8db273295124ed, 0x00000000000000000000000084c2b16fa6877a8ff4f3271db7ea837233dfd6f0, 0000000000000000000000000000000000000000000000000000000000004e20 )
236 AdminUpgradeabilityProxy.0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15( 0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15, 0x000000000000000000000000f8f92b7af05d01f76aa84d3fbf8db273295124ed, 0000000000000000000000000000000000000000000000000000000000004e20, 0000000000000000000000000000000000000000000000000000000000000004 )
237 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000f8f92b7af05d01f76aa84d3fbf8db273295124ed, 0x00000000000000000000000084c2b16fa6877a8ff4f3271db7ea837233dfd6f0, 0000000000000000000000000000000000000000000000000000000000004e20 )

Account State Difference:

  Address   Before After State Difference Code
0x84c2b16F...233DFd6f0
0xA0b86991...E3606eB48
(Flashbots: Builder)
1.187707571203607149 Eth1.188206627203607149 Eth0.000499056
0xf8F92B7A...3295124ED
0.00640365586255806 Eth
Nonce: 329
0.00186401863332574 Eth
Nonce: 330
0.00453963722923232

Execution Trace

AdminUpgradeabilityProxy.4a970be7( )
  • RibbonEarnVault.depositWithPermit( amount=20000, deadline=1667528193, v=27, r=AFEE8C0F03CE34227E85766A102CD080C49EF8BB3B3E3E255107E26121CEF543, s=5EF152E0CD9968E1D8CF55D2AB07EE4033510B7AE03F0BD76548C417309EE86E )
    • FiatTokenProxy.d505accf( )
      • FiatTokenV2_1.permit( owner=0xf8F92B7Af05D01F76aA84d3FbF8dB273295124ED, spender=0x84c2b16FA6877a8fF4F3271db7ea837233DFd6f0, value=20000, deadline=1667528193, v=27, r=AFEE8C0F03CE34227E85766A102CD080C49EF8BB3B3E3E255107E26121CEF543, s=5EF152E0CD9968E1D8CF55D2AB07EE4033510B7AE03F0BD76548C417309EE86E )
        • Null: 0x000...001.0f9c64d5( )
        • FiatTokenProxy.70a08231( )
          • FiatTokenV2_1.balanceOf( account=0x84c2b16FA6877a8fF4F3271db7ea837233DFd6f0 ) => ( 162556477318 )
          • BeaconProxy.STATICCALL( )
            • UpgradeableBeacon.STATICCALL( )
            • PoolMaster.DELEGATECALL( )
              • FiatTokenProxy.70a08231( )
                • FiatTokenV2_1.balanceOf( account=0x0Aea75705Be8281f4c24c3E954D1F8b1D0f8044C ) => ( 6173595637480 )
                • CosineInterestRateModel.getBorrowRate( balance=6173595637480, borrows=24801943047375, reserves=105512832823 ) => ( 3094220749 )
                • FiatTokenProxy.70a08231( )
                  • FiatTokenV2_1.balanceOf( account=0x0Aea75705Be8281f4c24c3E954D1F8b1D0f8044C ) => ( 6173595637480 )
                  • FiatTokenProxy.70a08231( )
                    • FiatTokenV2_1.balanceOf( account=0x0Aea75705Be8281f4c24c3E954D1F8b1D0f8044C ) => ( 6173595637480 )
                    • FiatTokenProxy.70a08231( )
                      • FiatTokenV2_1.balanceOf( account=0x0Aea75705Be8281f4c24c3E954D1F8b1D0f8044C ) => ( 6173595637480 )
                      • BeaconProxy.70a08231( )
                        • UpgradeableBeacon.STATICCALL( )
                        • PoolMaster.balanceOf( account=0x84c2b16FA6877a8fF4F3271db7ea837233DFd6f0 ) => ( 12565017097403 )
                        • FiatTokenProxy.23b872dd( )
                          • FiatTokenV2_1.transferFrom( from=0xf8F92B7Af05D01F76aA84d3FbF8dB273295124ED, to=0x84c2b16FA6877a8fF4F3271db7ea837233DFd6f0, value=20000 ) => ( True )
                            File 1 of 8: AdminUpgradeabilityProxy
                            /**
                             *Submitted for verification at Etherscan.io on 2021-02-27
                            */
                            
                            // SPDX-License-Identifier: MIT
                            
                            pragma solidity >=0.6.8;
                            
                            
                            // 
                            /**
                             * @title Proxy
                             * @dev Implements delegation of calls to other contracts, with proper
                             * forwarding of return values and bubbling of failures.
                             * It defines a fallback function that delegates all calls to the address
                             * returned by the abstract _implementation() internal function.
                             */
                            abstract contract Proxy {
                              /**
                               * @dev Fallback function.
                               * Implemented entirely in `_fallback`.
                               */
                              fallback () payable external {
                                _fallback();
                              }
                            
                              /**
                               * @dev Receive function.
                               * Implemented entirely in `_fallback`.
                               */
                              receive () payable external {
                                // _fallback();
                              }
                            
                              /**
                               * @return The Address of the implementation.
                               */
                              function _implementation() internal virtual view returns (address);
                            
                              /**
                               * @dev Delegates execution to an implementation contract.
                               * This is a low level function that doesn't return to its internal call site.
                               * It will return to the external caller whatever the implementation returns.
                               * @param implementation Address to delegate.
                               */
                              function _delegate(address implementation) internal {
                                assembly {
                                  // Copy msg.data. We take full control of memory in this inline assembly
                                  // block because it will not return to Solidity code. We overwrite the
                                  // Solidity scratch pad at memory position 0.
                                  calldatacopy(0, 0, calldatasize())
                            
                                  // Call the implementation.
                                  // out and outsize are 0 because we don't know the size yet.
                                  let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                            
                                  // Copy the returned data.
                                  returndatacopy(0, 0, returndatasize())
                            
                                  switch result
                                  // delegatecall returns 0 on error.
                                  case 0 { revert(0, returndatasize()) }
                                  default { return(0, returndatasize()) }
                                }
                              }
                            
                              /**
                               * @dev Function that is run as the first thing in the fallback function.
                               * Can be redefined in derived contracts to add functionality.
                               * Redefinitions must call super._willFallback().
                               */
                              function _willFallback() internal virtual {
                              }
                            
                              /**
                               * @dev fallback implementation.
                               * Extracted to enable manual triggering.
                               */
                              function _fallback() internal {
                                _willFallback();
                                _delegate(_implementation());
                              }
                            }
                            
                            // 
                            /**
                             * @dev Collection of functions related to the address type
                             */
                            library Address {
                                /**
                                 * @dev Returns true if `account` is a contract.
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * It is unsafe to assume that an address for which this function returns
                                 * false is an externally-owned account (EOA) and not a contract.
                                 *
                                 * Among others, `isContract` will return false for the following
                                 * types of addresses:
                                 *
                                 *  - an externally-owned account
                                 *  - a contract in construction
                                 *  - an address where a contract will be created
                                 *  - an address where a contract lived, but was destroyed
                                 * ====
                                 */
                                function isContract(address account) internal view returns (bool) {
                                    // This method relies on extcodesize, which returns 0 for contracts in
                                    // construction, since the code is only stored at the end of the
                                    // constructor execution.
                            
                                    uint256 size;
                                    // solhint-disable-next-line no-inline-assembly
                                    assembly { size := extcodesize(account) }
                                    return size > 0;
                                }
                            
                                /**
                                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                                 * `recipient`, forwarding all available gas and reverting on errors.
                                 *
                                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                                 * imposed by `transfer`, making them unable to receive funds via
                                 * `transfer`. {sendValue} removes this limitation.
                                 *
                                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                                 *
                                 * IMPORTANT: because control is transferred to `recipient`, care must be
                                 * taken to not create reentrancy vulnerabilities. Consider using
                                 * {ReentrancyGuard} or the
                                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                                 */
                                function sendValue(address payable recipient, uint256 amount) internal {
                                    require(address(this).balance >= amount, "Address: insufficient balance");
                            
                                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                    (bool success, ) = recipient.call{ value: amount }("");
                                    require(success, "Address: unable to send value, recipient may have reverted");
                                }
                            
                                /**
                                 * @dev Performs a Solidity function call using a low level `call`. A
                                 * plain`call` is an unsafe replacement for a function call: use this
                                 * function instead.
                                 *
                                 * If `target` reverts with a revert reason, it is bubbled up by this
                                 * function (like regular Solidity function calls).
                                 *
                                 * Returns the raw returned data. To convert to the expected return value,
                                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                                 *
                                 * Requirements:
                                 *
                                 * - `target` must be a contract.
                                 * - calling `target` with `data` must not revert.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionCall(target, data, "Address: low-level call failed");
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                                 * `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, 0, errorMessage);
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but also transferring `value` wei to `target`.
                                 *
                                 * Requirements:
                                 *
                                 * - the calling contract must have an ETH balance of at least `value`.
                                 * - the called Solidity function must be `payable`.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                                    require(address(this).balance >= value, "Address: insufficient balance for call");
                                    require(isContract(target), "Address: call to non-contract");
                            
                                    // solhint-disable-next-line avoid-low-level-calls
                                    (bool success, bytes memory returndata) = target.call{ value: value }(data);
                                    return _verifyCallResult(success, returndata, errorMessage);
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                    return functionStaticCall(target, data, "Address: low-level static call failed");
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                                    require(isContract(target), "Address: static call to non-contract");
                            
                                    // solhint-disable-next-line avoid-low-level-calls
                                    (bool success, bytes memory returndata) = target.staticcall(data);
                                    return _verifyCallResult(success, returndata, errorMessage);
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a delegate call.
                                 *
                                 * _Available since v3.4._
                                 */
                                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a delegate call.
                                 *
                                 * _Available since v3.4._
                                 */
                                function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                                    require(isContract(target), "Address: delegate call to non-contract");
                            
                                    // solhint-disable-next-line avoid-low-level-calls
                                    (bool success, bytes memory returndata) = target.delegatecall(data);
                                    return _verifyCallResult(success, returndata, errorMessage);
                                }
                            
                                function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                                    if (success) {
                                        return returndata;
                                    } else {
                                        // Look for revert reason and bubble it up if present
                                        if (returndata.length > 0) {
                                            // The easiest way to bubble the revert reason is using memory via assembly
                            
                                            // solhint-disable-next-line no-inline-assembly
                                            assembly {
                                                let returndata_size := mload(returndata)
                                                revert(add(32, returndata), returndata_size)
                                            }
                                        } else {
                                            revert(errorMessage);
                                        }
                                    }
                                }
                            }
                            
                            // 
                            /**
                             * @title UpgradeabilityProxy
                             * @dev This contract implements a proxy that allows to change the
                             * implementation address to which it will delegate.
                             * Such a change is called an implementation upgrade.
                             */
                            contract UpgradeabilityProxy is Proxy {
                              /**
                               * @dev Contract constructor.
                               * @param _logic Address of the initial implementation.
                               * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                               * It should include the signature and the parameters of the function to be called, as described in
                               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                               * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                               */
                              constructor(address _logic, bytes memory _data) payable {
                                assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                                _setImplementation(_logic);
                                if(_data.length > 0) {
                                  (bool success,) = _logic.delegatecall(_data);
                                  require(success);
                                }
                              }  
                            
                              /**
                               * @dev Emitted when the implementation is upgraded.
                               * @param implementation Address of the new implementation.
                               */
                              event Upgraded(address indexed implementation);
                            
                              /**
                               * @dev Storage slot with the address of the current implementation.
                               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                            
                              /**
                               * @dev Returns the current implementation.
                               * @return impl Address of the current implementation
                               */
                              function _implementation() internal override view returns (address impl) {
                                bytes32 slot = IMPLEMENTATION_SLOT;
                                assembly {
                                  impl := sload(slot)
                                }
                              }
                            
                              /**
                               * @dev Upgrades the proxy to a new implementation.
                               * @param newImplementation Address of the new implementation.
                               */
                              function _upgradeTo(address newImplementation) internal {
                                _setImplementation(newImplementation);
                                emit Upgraded(newImplementation);
                              }
                            
                              /**
                               * @dev Sets the implementation address of the proxy.
                               * @param newImplementation Address of the new implementation.
                               */
                              function _setImplementation(address newImplementation) internal {
                                require(Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
                            
                                bytes32 slot = IMPLEMENTATION_SLOT;
                            
                                assembly {
                                  sstore(slot, newImplementation)
                                }
                              }
                            }
                            
                            // 
                            /**
                             * @title AdminUpgradeabilityProxy
                             * @dev This contract combines an upgradeability proxy with an authorization
                             * mechanism for administrative tasks.
                             * All external functions in this contract must be guarded by the
                             * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                             * feature proposal that would enable this to be done automatically.
                             */
                            contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
                              /**
                               * Contract constructor.
                               * @param _logic address of the initial implementation.
                               * @param _admin Address of the proxy administrator.
                               * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                               * It should include the signature and the parameters of the function to be called, as described in
                               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                               * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                               */
                              constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) payable {
                                assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
                                _setAdmin(_admin);
                              }
                            
                              /**
                               * @dev Emitted when the administration has been transferred.
                               * @param previousAdmin Address of the previous admin.
                               * @param newAdmin Address of the new admin.
                               */
                              event AdminChanged(address previousAdmin, address newAdmin);
                            
                              /**
                               * @dev Storage slot with the admin of the contract.
                               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                               * validated in the constructor.
                               */
                            
                              bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                            
                              /**
                               * @dev Modifier to check whether the `msg.sender` is the admin.
                               * If it is, it will run the function. Otherwise, it will delegate the call
                               * to the implementation.
                               */
                              modifier ifAdmin() {
                                if (msg.sender == _admin()) {
                                  _;
                                } else {
                                  _fallback();
                                }
                              }
                            
                              /**
                               * @return The address of the proxy admin.
                               */
                              function admin() external ifAdmin returns (address) {
                                return _admin();
                              }
                            
                              /**
                               * @return The address of the implementation.
                               */
                              function implementation() external ifAdmin returns (address) {
                                return _implementation();
                              }
                            
                              /**
                               * @dev Changes the admin of the proxy.
                               * Only the current admin can call this function.
                               * @param newAdmin Address to transfer proxy administration to.
                               */
                              function changeAdmin(address newAdmin) external ifAdmin {
                                require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                                emit AdminChanged(_admin(), newAdmin);
                                _setAdmin(newAdmin);
                              }
                            
                              /**
                               * @dev Upgrade the backing implementation of the proxy.
                               * Only the admin can call this function.
                               * @param newImplementation Address of the new implementation.
                               */
                              function upgradeTo(address newImplementation) external ifAdmin {
                                _upgradeTo(newImplementation);
                              }
                            
                              /**
                               * @dev Upgrade the backing implementation of the proxy and call a function
                               * on the new implementation.
                               * This is useful to initialize the proxied contract.
                               * @param newImplementation Address of the new implementation.
                               * @param data Data to send as msg.data in the low level call.
                               * It should include the signature and the parameters of the function to be called, as described in
                               * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                               */
                              function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
                                _upgradeTo(newImplementation);
                                (bool success,) = newImplementation.delegatecall(data);
                                require(success);
                              }
                            
                              /**
                               * @return adm The admin slot.
                               */
                              function _admin() internal view returns (address adm) {
                                bytes32 slot = ADMIN_SLOT;
                                assembly {
                                  adm := sload(slot)
                                }
                              }
                            
                              /**
                               * @dev Sets the address of the proxy admin.
                               * @param newAdmin Address of the new proxy admin.
                               */
                              function _setAdmin(address newAdmin) internal {
                                bytes32 slot = ADMIN_SLOT;
                            
                                assembly {
                                  sstore(slot, newAdmin)
                                }
                              }
                            
                              /**
                               * @dev Only fall back when the sender is not the admin.
                               */
                              function _willFallback() internal override virtual {
                                require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                                super._willFallback();
                              }
                            }

                            File 2 of 8: FiatTokenProxy
                            pragma solidity ^0.4.24;
                            
                            // File: zos-lib/contracts/upgradeability/Proxy.sol
                            
                            /**
                             * @title Proxy
                             * @dev Implements delegation of calls to other contracts, with proper
                             * forwarding of return values and bubbling of failures.
                             * It defines a fallback function that delegates all calls to the address
                             * returned by the abstract _implementation() internal function.
                             */
                            contract Proxy {
                              /**
                               * @dev Fallback function.
                               * Implemented entirely in `_fallback`.
                               */
                              function () payable external {
                                _fallback();
                              }
                            
                              /**
                               * @return The Address of the implementation.
                               */
                              function _implementation() internal view returns (address);
                            
                              /**
                               * @dev Delegates execution to an implementation contract.
                               * This is a low level function that doesn't return to its internal call site.
                               * It will return to the external caller whatever the implementation returns.
                               * @param implementation Address to delegate.
                               */
                              function _delegate(address implementation) internal {
                                assembly {
                                  // Copy msg.data. We take full control of memory in this inline assembly
                                  // block because it will not return to Solidity code. We overwrite the
                                  // Solidity scratch pad at memory position 0.
                                  calldatacopy(0, 0, calldatasize)
                            
                                  // Call the implementation.
                                  // out and outsize are 0 because we don't know the size yet.
                                  let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
                            
                                  // Copy the returned data.
                                  returndatacopy(0, 0, returndatasize)
                            
                                  switch result
                                  // delegatecall returns 0 on error.
                                  case 0 { revert(0, returndatasize) }
                                  default { return(0, returndatasize) }
                                }
                              }
                            
                              /**
                               * @dev Function that is run as the first thing in the fallback function.
                               * Can be redefined in derived contracts to add functionality.
                               * Redefinitions must call super._willFallback().
                               */
                              function _willFallback() internal {
                              }
                            
                              /**
                               * @dev fallback implementation.
                               * Extracted to enable manual triggering.
                               */
                              function _fallback() internal {
                                _willFallback();
                                _delegate(_implementation());
                              }
                            }
                            
                            // File: openzeppelin-solidity/contracts/AddressUtils.sol
                            
                            /**
                             * Utility library of inline functions on addresses
                             */
                            library AddressUtils {
                            
                              /**
                               * Returns whether the target address is a contract
                               * @dev This function will return false if invoked during the constructor of a contract,
                               * as the code is not actually created until after the constructor finishes.
                               * @param addr address to check
                               * @return whether the target address is a contract
                               */
                              function isContract(address addr) internal view returns (bool) {
                                uint256 size;
                                // XXX Currently there is no better way to check if there is a contract in an address
                                // than to check the size of the code at that address.
                                // See https://ethereum.stackexchange.com/a/14016/36603
                                // for more details about how this works.
                                // TODO Check this again before the Serenity release, because all addresses will be
                                // contracts then.
                                // solium-disable-next-line security/no-inline-assembly
                                assembly { size := extcodesize(addr) }
                                return size > 0;
                              }
                            
                            }
                            
                            // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
                            
                            /**
                             * @title UpgradeabilityProxy
                             * @dev This contract implements a proxy that allows to change the
                             * implementation address to which it will delegate.
                             * Such a change is called an implementation upgrade.
                             */
                            contract UpgradeabilityProxy is Proxy {
                              /**
                               * @dev Emitted when the implementation is upgraded.
                               * @param implementation Address of the new implementation.
                               */
                              event Upgraded(address implementation);
                            
                              /**
                               * @dev Storage slot with the address of the current implementation.
                               * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
                               * validated in the constructor.
                               */
                              bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
                            
                              /**
                               * @dev Contract constructor.
                               * @param _implementation Address of the initial implementation.
                               */
                              constructor(address _implementation) public {
                                assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
                            
                                _setImplementation(_implementation);
                              }
                            
                              /**
                               * @dev Returns the current implementation.
                               * @return Address of the current implementation
                               */
                              function _implementation() internal view returns (address impl) {
                                bytes32 slot = IMPLEMENTATION_SLOT;
                                assembly {
                                  impl := sload(slot)
                                }
                              }
                            
                              /**
                               * @dev Upgrades the proxy to a new implementation.
                               * @param newImplementation Address of the new implementation.
                               */
                              function _upgradeTo(address newImplementation) internal {
                                _setImplementation(newImplementation);
                                emit Upgraded(newImplementation);
                              }
                            
                              /**
                               * @dev Sets the implementation address of the proxy.
                               * @param newImplementation Address of the new implementation.
                               */
                              function _setImplementation(address newImplementation) private {
                                require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
                            
                                bytes32 slot = IMPLEMENTATION_SLOT;
                            
                                assembly {
                                  sstore(slot, newImplementation)
                                }
                              }
                            }
                            
                            // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
                            
                            /**
                             * @title AdminUpgradeabilityProxy
                             * @dev This contract combines an upgradeability proxy with an authorization
                             * mechanism for administrative tasks.
                             * All external functions in this contract must be guarded by the
                             * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                             * feature proposal that would enable this to be done automatically.
                             */
                            contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
                              /**
                               * @dev Emitted when the administration has been transferred.
                               * @param previousAdmin Address of the previous admin.
                               * @param newAdmin Address of the new admin.
                               */
                              event AdminChanged(address previousAdmin, address newAdmin);
                            
                              /**
                               * @dev Storage slot with the admin of the contract.
                               * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
                               * validated in the constructor.
                               */
                              bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
                            
                              /**
                               * @dev Modifier to check whether the `msg.sender` is the admin.
                               * If it is, it will run the function. Otherwise, it will delegate the call
                               * to the implementation.
                               */
                              modifier ifAdmin() {
                                if (msg.sender == _admin()) {
                                  _;
                                } else {
                                  _fallback();
                                }
                              }
                            
                              /**
                               * Contract constructor.
                               * It sets the `msg.sender` as the proxy administrator.
                               * @param _implementation address of the initial implementation.
                               */
                              constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
                                assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
                            
                                _setAdmin(msg.sender);
                              }
                            
                              /**
                               * @return The address of the proxy admin.
                               */
                              function admin() external view ifAdmin returns (address) {
                                return _admin();
                              }
                            
                              /**
                               * @return The address of the implementation.
                               */
                              function implementation() external view ifAdmin returns (address) {
                                return _implementation();
                              }
                            
                              /**
                               * @dev Changes the admin of the proxy.
                               * Only the current admin can call this function.
                               * @param newAdmin Address to transfer proxy administration to.
                               */
                              function changeAdmin(address newAdmin) external ifAdmin {
                                require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                                emit AdminChanged(_admin(), newAdmin);
                                _setAdmin(newAdmin);
                              }
                            
                              /**
                               * @dev Upgrade the backing implementation of the proxy.
                               * Only the admin can call this function.
                               * @param newImplementation Address of the new implementation.
                               */
                              function upgradeTo(address newImplementation) external ifAdmin {
                                _upgradeTo(newImplementation);
                              }
                            
                              /**
                               * @dev Upgrade the backing implementation of the proxy and call a function
                               * on the new implementation.
                               * This is useful to initialize the proxied contract.
                               * @param newImplementation Address of the new implementation.
                               * @param data Data to send as msg.data in the low level call.
                               * It should include the signature and the parameters of the function to be
                               * called, as described in
                               * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
                               */
                              function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
                                _upgradeTo(newImplementation);
                                require(address(this).call.value(msg.value)(data));
                              }
                            
                              /**
                               * @return The admin slot.
                               */
                              function _admin() internal view returns (address adm) {
                                bytes32 slot = ADMIN_SLOT;
                                assembly {
                                  adm := sload(slot)
                                }
                              }
                            
                              /**
                               * @dev Sets the address of the proxy admin.
                               * @param newAdmin Address of the new proxy admin.
                               */
                              function _setAdmin(address newAdmin) internal {
                                bytes32 slot = ADMIN_SLOT;
                            
                                assembly {
                                  sstore(slot, newAdmin)
                                }
                              }
                            
                              /**
                               * @dev Only fall back when the sender is not the admin.
                               */
                              function _willFallback() internal {
                                require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                                super._willFallback();
                              }
                            }
                            
                            // File: contracts/FiatTokenProxy.sol
                            
                            /**
                            * Copyright CENTRE SECZ 2018
                            *
                            * Permission is hereby granted, free of charge, to any person obtaining a copy 
                            * of this software and associated documentation files (the "Software"), to deal 
                            * in the Software without restriction, including without limitation the rights 
                            * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
                            * copies of the Software, and to permit persons to whom the Software is furnished to 
                            * do so, subject to the following conditions:
                            *
                            * The above copyright notice and this permission notice shall be included in all 
                            * copies or substantial portions of the Software.
                            *
                            * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
                            * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
                            * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
                            * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
                            * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
                            * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                            */
                            
                            pragma solidity ^0.4.24;
                            
                            
                            /**
                             * @title FiatTokenProxy
                             * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
                            */ 
                            contract FiatTokenProxy is AdminUpgradeabilityProxy {
                                constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
                                }
                            }

                            File 3 of 8: RibbonEarnVault
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                            import {IERC20Detailed} from "../../interfaces/IERC20Detailed.sol";
                            import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
                            import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
                            import {
                                ReentrancyGuardUpgradeable
                            } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
                            import {
                                OwnableUpgradeable
                            } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
                            import {
                                ERC20Upgradeable
                            } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
                            import {
                                SafeERC20
                            } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                            import {IWETH} from "../../interfaces/IWETH.sol";
                            import {RibbonEarnVaultStorage} from "../../storage/RibbonEarnVaultStorage.sol";
                            import {Vault} from "../../libraries/Vault.sol";
                            import {VaultLifecycleEarn} from "../../libraries/VaultLifecycleEarn.sol";
                            import {ShareMath} from "../../libraries/ShareMath.sol";
                            import {ILiquidityGauge} from "../../interfaces/ILiquidityGauge.sol";
                            import {IVaultPauser} from "../../interfaces/IVaultPauser.sol";
                            import {IRibbonLend} from "../../interfaces/IRibbonLend.sol";
                            /**
                             * Earn Vault Error Codes
                             * R1: loan allocation in USD must be 0
                             * R2: option allocation in USD must be 0
                             * R3: invalid owner address
                             * R4: msg.sender is not keeper
                             * R5: msg.sender borrower weight is 0
                             * R6: msg.sender is not option seller
                             * R7: invalid keeper address
                             * R8: invalid fee recipient address
                             * R9: invalid option seller
                             * R10: time lock still active
                             * R11: management fee greater than 100%
                             * R12: performance fee greater than 100%
                             * R13: deposit cap is zero
                             * R14: loan allocation is greater than 100%
                             * R15: loan term length is less than a day
                             * R16: option purchase frequency is zero
                             * R17: option purchase frequency is greater than loan term length
                             * R18: cannot use depositETH in non-eth vault
                             * R19: cannot use depositETH with msg.value = 0
                             * R20: vault asset is not USDC
                             * R21: deposit amount is 0
                             * R22: deposit amount exceeds vault cap
                             * R23: deposit amount less than minimum supply
                             * R24: cannot initiate withdraw on 0 shares
                             * R25: a withdraw has already been initiated
                             * R26: cannot complete withdraw when not initiated
                             * R27: cannot complete withdraw when round not closed yet
                             * R28: withdraw amount in complete withdraw is zero
                             * R29: cannot redeem zero shares
                             * R30: cannot redeem more shares than available
                             * R31: cannot instantly withdraw zero
                             * R32: cannot withdraw in current round
                             * R33: exceeding amount withdrawable instantly
                             * R34: purchasing option to early since last purchase\u2028 * R35: vault asset not recoverable
                             * R36: vault share not recoverable
                             * R37: recipient cannot be vault
                             * R38: transfer failed\u2028 * R39: premature roll to next round
                             * R40: array length mismatch
                             * R41: invalid token name
                             * R42: invalid token symbol
                             * R43: invalid vault asset
                             * R44: invalid vault minimum supply
                             * R45: deposit cap must be higher than minimum supply
                             * R46: next loan term length must be 0
                             * R47: next option purchase frequency must be 0
                             * R48: current loan term length must be >= 1 day
                             * R49: current option purchase freq must be < loan term length
                             * R50: loan pct + option pct == total PCT
                             * R51: invalid pending option seller
                             */
                            /**
                             * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe
                             * the inheritance chain closely.
                             * Any changes/appends in storage variable needs to happen in RibbonEarnVaultStorage.
                             * RibbonEarnVault should not inherit from any other contract aside from RibbonVault, RibbonEarnVaultStorage
                             */
                            contract RibbonEarnVault is
                                ReentrancyGuardUpgradeable,
                                OwnableUpgradeable,
                                ERC20Upgradeable,
                                RibbonEarnVaultStorage
                            {
                                using SafeERC20 for IERC20;
                                using ShareMath for Vault.DepositReceipt;
                                // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE
                                // This is to prevent storage collisions. All storage variables should be appended to RibbonEarnVaultStorage.
                                // Read this documentation to learn more:
                                // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts
                                /************************************************
                                 *  IMMUTABLES & CONSTANTS
                                 ***********************************************/
                                /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
                                address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
                                uint32 public constant TOTAL_PCT = 1000000; // Equals 100%
                                /************************************************
                                 *  EVENTS
                                 ***********************************************/
                                event Deposit(address indexed account, uint256 amount, uint256 round);
                                event InitiateWithdraw(
                                    address indexed account,
                                    uint256 shares,
                                    uint256 round
                                );
                                event Redeem(address indexed account, uint256 share, uint256 round);
                                event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);
                                event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);
                                event CapSet(uint256 oldCap, uint256 newCap);
                                event BorrowerBasketUpdated(address[] borrowers, uint128[] borrowerWeights);
                                event CommitBorrowerBasket(uint256 totalBorrowerWeight);
                                event OptionSellerSet(address oldOptionSeller, address newOptionSeller);
                                event NewAllocationSet(
                                    uint256 oldLoanAllocation,
                                    uint256 oldOptionAllocation,
                                    uint256 newLoanAllocation,
                                    uint256 newOptionAllocation
                                );
                                event NewLoanTermLength(
                                    uint256 oldLoanTermLength,
                                    uint256 newLoanTermLength
                                );
                                event NewOptionPurchaseFrequency(
                                    uint256 oldOptionPurchaseFrequency,
                                    uint256 newOptionPurchaseFrequency
                                );
                                event Withdraw(address indexed account, uint256 amount, uint256 shares);
                                event CollectVaultFees(
                                    uint256 performanceFee,
                                    uint256 vaultFee,
                                    uint256 round,
                                    address indexed feeRecipient
                                );
                                event PurchaseOption(uint256 premium, address indexed seller);
                                event PayOptionYield(
                                    uint256 yield,
                                    uint256 netYield,
                                    address indexed seller
                                );
                                event InstantWithdraw(
                                    address indexed account,
                                    uint256 amount,
                                    uint256 round
                                );
                                /************************************************
                                 *  STRUCTS
                                 ***********************************************/
                                /**
                                 * @notice Initialization parameters for the vault.
                                 * @param _owner is the owner of the vault with critical permissions
                                 * @param _feeRecipient is the address to recieve vault performance and management fees
                                 * @param _borrowers is the addresses of the basket of borrowing entities (EX: Wintermute, GSR, Alameda, Genesis)
                                 * @param _borrowerWeights is the borrow weight of the addresses
                                 * @param _optionSeller is the address of the entity that we will be buying options from (EX: Orbit)
                                 * @param _managementFee is the management fee pct.
                                 * @param _performanceFee is the perfomance fee pct.
                                 * @param _tokenName is the name of the token
                                 * @param _tokenSymbol is the symbol of the token
                                 */
                                struct InitParams {
                                    address _owner;
                                    address _keeper;
                                    address[] _borrowers;
                                    uint128[] _borrowerWeights;
                                    address _optionSeller;
                                    address _feeRecipient;
                                    uint256 _managementFee;
                                    uint256 _performanceFee;
                                    string _tokenName;
                                    string _tokenSymbol;
                                }
                                /************************************************
                                 *  CONSTRUCTOR & INITIALIZATION
                                 ***********************************************/
                                /**
                                 * @notice Initializes the OptionVault contract with storage variables.
                                 * @param _initParams is the struct with vault initialization parameters
                                 * @param _vaultParams is the struct with vault general data
                                 * @param _allocationState is the struct with vault loan/option allocation data
                                 */
                                function initialize(
                                    InitParams calldata _initParams,
                                    Vault.VaultParams calldata _vaultParams,
                                    Vault.AllocationState calldata _allocationState
                                ) external initializer {
                                    require(_initParams._owner != address(0), "R3");
                                    VaultLifecycleEarn.verifyInitializerParams(
                                        _initParams._keeper,
                                        _initParams._feeRecipient,
                                        _initParams._optionSeller,
                                        _initParams._managementFee,
                                        _initParams._performanceFee,
                                        _initParams._tokenName,
                                        _initParams._tokenSymbol,
                                        _vaultParams,
                                        _allocationState,
                                        TOTAL_PCT
                                    );
                                    __ReentrancyGuard_init();
                                    __ERC20_init(_initParams._tokenName, _initParams._tokenSymbol);
                                    __Ownable_init();
                                    transferOwnership(_initParams._owner);
                                    keeper = _initParams._keeper;
                                    feeRecipient = _initParams._feeRecipient;
                                    optionSeller = _initParams._optionSeller;
                                    performanceFee = _initParams._performanceFee;
                                    managementFee =
                                        (_initParams._managementFee * Vault.FEE_MULTIPLIER) /
                                        ((365 days * Vault.FEE_MULTIPLIER) /
                                            _allocationState.currentLoanTermLength);
                                    vaultParams = _vaultParams;
                                    allocationState = _allocationState;
                                    _updateBorrowerBasket(
                                        _initParams._borrowers,
                                        _initParams._borrowerWeights
                                    );
                                    uint256 assetBalance = totalBalance();
                                    ShareMath.assertUint104(assetBalance);
                                    vaultState.lastLockedAmount = uint104(assetBalance);
                                    vaultState.round = 1;
                                }
                                /**
                                 * @dev Throws if called by any account other than the keeper.
                                 */
                                modifier onlyKeeper() {
                                    require(msg.sender == keeper, "R4");
                                    _;
                                }
                                /**
                                 * @dev Throws if called by any account other than the borrower.
                                 */
                                modifier onlyBorrower() {
                                    require(borrowerWeights[msg.sender].borrowerWeight > 0, "R5");
                                    _;
                                }
                                /**
                                 * @dev Throws if called by any account other than the option seller.
                                 */
                                modifier onlyOptionSeller() {
                                    require(msg.sender == optionSeller, "R6");
                                    _;
                                }
                                /************************************************
                                 *  SETTERS
                                 ***********************************************/
                                /**
                                 * @notice Sets the new keeper
                                 * @param newKeeper is the address of the new keeper
                                 */
                                function setNewKeeper(address newKeeper) external onlyOwner {
                                    require(newKeeper != address(0), "R7");
                                    keeper = newKeeper;
                                }
                                /**
                                 * @notice Sets the new fee recipient
                                 * @param newFeeRecipient is the address of the new fee recipient
                                 */
                                function setFeeRecipient(address newFeeRecipient) external onlyOwner {
                                    require(newFeeRecipient != address(0), "R8");
                                    feeRecipient = newFeeRecipient;
                                }
                                /**
                                 * @notice Updates the basket of borrowers (this overrides current pending update to basket)
                                 * @param borrowers is the array of borrowers to update
                                 * @param borrowerWeights is the array of corresponding borrow weights for the borrower
                                 */
                                function updateBorrowerBasket(
                                    address[] calldata borrowers,
                                    uint128[] calldata borrowerWeights
                                ) external onlyOwner {
                                    _updateBorrowerBasket(borrowers, borrowerWeights);
                                    lastBorrowerBasketChange = block.timestamp;
                                }
                                /**
                                 * @notice Sets the new option seller
                                 * @param newOptionSeller is the address of the new option seller
                                 */
                                function setOptionSeller(address newOptionSeller) external onlyOwner {
                                    require(newOptionSeller != address(0), "R9");
                                    emit OptionSellerSet(optionSeller, newOptionSeller);
                                    pendingOptionSeller = newOptionSeller;
                                    lastOptionSellerChange = block.timestamp;
                                }
                                /**
                                 * @notice Commits the option seller
                                 */
                                function commitOptionSeller() external onlyOwner {
                                    require(pendingOptionSeller != address(0), "R51");
                                    optionSeller = pendingOptionSeller;
                                    pendingOptionSeller = address(0);
                                }
                                /**
                                 * @notice Sets the management fee for the vault
                                 * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%
                                 */
                                function setManagementFee(uint256 newManagementFee) external onlyOwner {
                                    require(newManagementFee < 100 * Vault.FEE_MULTIPLIER, "R11");
                                    // We are dividing annualized management fee by loanTermLength
                                    uint256 tmpManagementFee =
                                        (newManagementFee * Vault.FEE_MULTIPLIER) /
                                            ((365 days * Vault.FEE_MULTIPLIER) /
                                                allocationState.currentLoanTermLength);
                                    emit ManagementFeeSet(managementFee, tmpManagementFee);
                                    managementFee = tmpManagementFee;
                                }
                                /**
                                 * @notice Sets the performance fee for the vault
                                 * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%
                                 */
                                function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {
                                    require(newPerformanceFee < 100 * Vault.FEE_MULTIPLIER, "R12");
                                    emit PerformanceFeeSet(performanceFee, newPerformanceFee);
                                    performanceFee = newPerformanceFee;
                                }
                                /**
                                 * @notice Sets a new cap for deposits
                                 * @param newCap is the new cap for deposits
                                 */
                                function setCap(uint256 newCap) external onlyOwner {
                                    require(newCap > 0, "R13");
                                    ShareMath.assertUint104(newCap);
                                    emit CapSet(vaultParams.cap, newCap);
                                    vaultParams.cap = uint104(newCap);
                                }
                                /**
                                 * @notice Sets new loan and option allocation percentage
                                 * @dev Can be called by admin
                                 * @param _loanAllocationPCT new allocation for loan
                                 * @param _optionAllocationPCT new allocation for option
                                 */
                                function setAllocationPCT(
                                    uint32 _loanAllocationPCT,
                                    uint32 _optionAllocationPCT
                                ) external onlyOwner {
                                    require(_loanAllocationPCT + _optionAllocationPCT <= TOTAL_PCT, "R14");
                                    emit NewAllocationSet(
                                        uint256(allocationState.loanAllocationPCT),
                                        uint256(_loanAllocationPCT),
                                        uint256(allocationState.optionAllocationPCT),
                                        uint256(_optionAllocationPCT)
                                    );
                                    allocationState.loanAllocationPCT = _loanAllocationPCT;
                                    allocationState.optionAllocationPCT = _optionAllocationPCT;
                                }
                                /**
                                 * @notice Sets loan term length
                                 * @dev Can be called by admin
                                 * @param _loanTermLength new loan term length
                                 */
                                function setLoanTermLength(uint32 _loanTermLength) external onlyOwner {
                                    require(_loanTermLength >= 1 days, "R15");
                                    allocationState.nextLoanTermLength = _loanTermLength;
                                    emit NewLoanTermLength(
                                        allocationState.currentLoanTermLength,
                                        _loanTermLength
                                    );
                                }
                                /**
                                 * @notice Sets option purchase frequency
                                 * @dev Can be called by admin
                                 * @param _optionPurchaseFreq new option purchase frequency
                                 */
                                function setOptionPurchaseFrequency(uint32 _optionPurchaseFreq)
                                    external
                                    onlyOwner
                                {
                                    require(_optionPurchaseFreq > 0, "R16");
                                    require(
                                        (allocationState.nextLoanTermLength == 0 &&
                                            _optionPurchaseFreq <= allocationState.currentLoanTermLength) ||
                                            _optionPurchaseFreq <= allocationState.nextLoanTermLength,
                                        "R17"
                                    );
                                    allocationState.nextOptionPurchaseFreq = _optionPurchaseFreq;
                                    emit NewOptionPurchaseFrequency(
                                        allocationState.currentOptionPurchaseFreq,
                                        _optionPurchaseFreq
                                    );
                                }
                                /**
                                 * @notice Sets the new liquidityGauge contract for this vault
                                 * @param newLiquidityGauge is the address of the new liquidityGauge contract
                                 */
                                function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {
                                    liquidityGauge = newLiquidityGauge;
                                }
                                /**
                                 * @notice Sets the new Vault Pauser contract for this vault
                                 * @param newVaultPauser is the address of the new vaultPauser contract
                                 */
                                function setVaultPauser(address newVaultPauser) external onlyOwner {
                                    vaultPauser = newVaultPauser;
                                }
                                /************************************************
                                 *  DEPOSIT & WITHDRAWALS
                                 ***********************************************/
                                /**
                                 * @notice Deposits the `asset` from msg.sender without an approve
                                 * `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                                 * over the EIP712-formatted function arguments
                                 * @param amount is the amount of `asset` to deposit
                                 * @param deadline must be a timestamp in the future
                                 * @param v is a valid signature
                                 * @param r is a valid signature
                                 * @param s is a valid signature
                                 */
                                function depositWithPermit(
                                    uint256 amount,
                                    uint256 deadline,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external nonReentrant {
                                    require(vaultParams.asset == USDC, "R20");
                                    require(amount > 0, "R21");
                                    // Sign for transfer approval
                                    IERC20Permit(vaultParams.asset).permit(
                                        msg.sender,
                                        address(this),
                                        amount,
                                        deadline,
                                        v,
                                        r,
                                        s
                                    );
                                    _depositFor(amount, msg.sender);
                                    // An approve() by the msg.sender is required beforehand
                                    IERC20(vaultParams.asset).safeTransferFrom(
                                        msg.sender,
                                        address(this),
                                        amount
                                    );
                                }
                                /**
                                 * @notice Deposits the `asset` from msg.sender.
                                 * @param amount is the amount of `asset` to deposit
                                 */
                                function deposit(uint256 amount) external nonReentrant {
                                    require(amount > 0, "R21");
                                    _depositFor(amount, msg.sender);
                                    // An approve() by the msg.sender is required beforehand
                                    IERC20(vaultParams.asset).safeTransferFrom(
                                        msg.sender,
                                        address(this),
                                        amount
                                    );
                                }
                                /**
                                 * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.
                                 * @notice Used for vault -> vault deposits on the user's behalf
                                 * @param amount is the amount of `asset` to deposit
                                 * @param creditor is the address that can claim/withdraw deposited amount
                                 */
                                function depositFor(uint256 amount, address creditor)
                                    external
                                    nonReentrant
                                {
                                    require(amount > 0, "R21");
                                    require(creditor != address(0));
                                    _depositFor(amount, creditor);
                                    // An approve() by the msg.sender is required beforehand
                                    IERC20(vaultParams.asset).safeTransferFrom(
                                        msg.sender,
                                        address(this),
                                        amount
                                    );
                                }
                                /**
                                 * @notice Mints the vault shares to the creditor
                                 * @param amount is the amount of `asset` deposited
                                 * @param creditor is the address to receieve the deposit
                                 */
                                function _depositFor(uint256 amount, address creditor) private {
                                    uint256 currentRound = vaultState.round;
                                    uint256 totalWithDepositedAmount = totalBalance() + amount;
                                    require(totalWithDepositedAmount <= vaultParams.cap, "R22");
                                    require(totalWithDepositedAmount >= vaultParams.minimumSupply, "R23");
                                    emit Deposit(creditor, amount, currentRound);
                                    Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];
                                    // If we have an unprocessed pending deposit from the previous rounds, we have to process it.
                                    uint256 unredeemedShares =
                                        depositReceipt.getSharesFromReceipt(
                                            currentRound,
                                            roundPricePerShare[depositReceipt.round],
                                            vaultParams.decimals
                                        );
                                    uint256 depositAmount = amount;
                                    // If we have a pending deposit in the current round, we add on to the pending deposit
                                    if (currentRound == depositReceipt.round) {
                                        uint256 newAmount = uint256(depositReceipt.amount) + amount;
                                        depositAmount = newAmount;
                                    }
                                    ShareMath.assertUint104(depositAmount);
                                    depositReceipts[creditor] = Vault.DepositReceipt({
                                        round: uint16(currentRound),
                                        amount: uint104(depositAmount),
                                        unredeemedShares: uint128(unredeemedShares)
                                    });
                                    uint256 newTotalPending = uint256(vaultState.totalPending) + amount;
                                    ShareMath.assertUint128(newTotalPending);
                                    vaultState.totalPending = uint128(newTotalPending);
                                }
                                /**
                                 * @notice Initiates a withdrawal that can be processed once the round completes
                                 * @param numShares is the number of shares to withdraw
                                 */
                                function _initiateWithdraw(uint256 numShares) internal {
                                    require(numShares > 0, "R24");
                                    // We do a max redeem before initiating a withdrawal
                                    // But we check if they must first have unredeemed shares
                                    if (
                                        depositReceipts[msg.sender].amount > 0 ||
                                        depositReceipts[msg.sender].unredeemedShares > 0
                                    ) {
                                        _redeem(0, true);
                                    }
                                    // This caches the `round` variable used in shareBalances
                                    uint256 currentRound = vaultState.round;
                                    Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];
                                    bool withdrawalIsSameRound = withdrawal.round == currentRound;
                                    emit InitiateWithdraw(msg.sender, numShares, currentRound);
                                    uint256 existingShares = uint256(withdrawal.shares);
                                    uint256 withdrawalShares;
                                    if (withdrawalIsSameRound) {
                                        withdrawalShares = existingShares + numShares;
                                    } else {
                                        require(existingShares == 0, "R25");
                                        withdrawalShares = numShares;
                                        withdrawal.round = uint16(currentRound);
                                    }
                                    ShareMath.assertUint128(withdrawalShares);
                                    withdrawal.shares = uint128(withdrawalShares);
                                    _transfer(msg.sender, address(this), numShares);
                                }
                                /**
                                 * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round
                                 * @return withdrawAmount the current withdrawal amount
                                 */
                                function _completeWithdraw() internal returns (uint256) {
                                    Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];
                                    uint256 withdrawalShares = withdrawal.shares;
                                    uint256 withdrawalRound = withdrawal.round;
                                    // This checks if there is a withdrawal
                                    require(withdrawalShares > 0, "R26");
                                    require(withdrawalRound < vaultState.round, "R27");
                                    // We leave the round number as non-zero to save on gas for subsequent writes
                                    withdrawal.shares = 0;
                                    vaultState.queuedWithdrawShares = uint128(
                                        uint256(vaultState.queuedWithdrawShares) - withdrawalShares
                                    );
                                    uint256 withdrawAmount =
                                        ShareMath.sharesToAsset(
                                            withdrawalShares,
                                            roundPricePerShare[withdrawalRound],
                                            vaultParams.decimals
                                        );
                                    emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);
                                    _burn(address(this), withdrawalShares);
                                    return withdrawAmount;
                                }
                                /**
                                 * @notice Redeems shares that are owed to the account
                                 * @param numShares is the number of shares to redeem
                                 */
                                function redeem(uint256 numShares) external nonReentrant {
                                    require(numShares > 0, "R29");
                                    _redeem(numShares, false);
                                }
                                /**
                                 * @notice Redeems the entire unredeemedShares balance that is owed to the account
                                 */
                                function maxRedeem() external nonReentrant {
                                    _redeem(0, true);
                                }
                                /**
                                 * @notice Redeems shares that are owed to the account
                                 * @param numShares is the number of shares to redeem, could be 0 when isMax=true
                                 * @param isMax is flag for when callers do a max redemption
                                 */
                                function _redeem(uint256 numShares, bool isMax) internal {
                                    Vault.DepositReceipt memory depositReceipt =
                                        depositReceipts[msg.sender];
                                    // This handles the null case when depositReceipt.round = 0
                                    // Because we start with round = 1 at `initialize`
                                    uint256 currentRound = vaultState.round;
                                    uint256 unredeemedShares =
                                        depositReceipt.getSharesFromReceipt(
                                            currentRound,
                                            roundPricePerShare[depositReceipt.round],
                                            vaultParams.decimals
                                        );
                                    numShares = isMax ? unredeemedShares : numShares;
                                    if (numShares == 0) {
                                        return;
                                    }
                                    require(numShares <= unredeemedShares, "R30");
                                    // If we have a depositReceipt on the same round, BUT we have some unredeemed shares
                                    // we debit from the unredeemedShares, but leave the amount field intact
                                    // If the round has past, with no new deposits, we just zero it out for new deposits.
                                    if (depositReceipt.round < currentRound) {
                                        depositReceipts[msg.sender].amount = 0;
                                    }
                                    ShareMath.assertUint128(numShares);
                                    depositReceipts[msg.sender].unredeemedShares = uint128(
                                        unredeemedShares - numShares
                                    );
                                    emit Redeem(msg.sender, numShares, depositReceipt.round);
                                    _transfer(address(this), msg.sender, numShares);
                                }
                                /**
                                 * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`
                                 * @param amount is the amount to withdraw
                                 */
                                function withdrawInstantly(uint256 amount) external nonReentrant {
                                    Vault.DepositReceipt storage depositReceipt =
                                        depositReceipts[msg.sender];
                                    uint256 currentRound = vaultState.round;
                                    require(amount > 0, "R31");
                                    require(depositReceipt.round == currentRound, "R32");
                                    uint256 receiptAmount = depositReceipt.amount;
                                    require(receiptAmount >= amount, "R33");
                                    // Subtraction underflow checks already ensure it is smaller than uint104
                                    depositReceipt.amount = uint104(receiptAmount - amount);
                                    vaultState.totalPending = uint128(
                                        uint256(vaultState.totalPending) - amount
                                    );
                                    emit InstantWithdraw(msg.sender, amount, currentRound);
                                    IERC20(vaultParams.asset).safeTransfer(msg.sender, amount);
                                }
                                /**
                                 * @notice Initiates a withdrawal that can be processed once the round completes
                                 * @param numShares is the number of shares to withdraw
                                 */
                                function initiateWithdraw(uint256 numShares) external nonReentrant {
                                    _initiateWithdraw(numShares);
                                    currentQueuedWithdrawShares = currentQueuedWithdrawShares + numShares;
                                }
                                /**
                                 * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round
                                 */
                                function completeWithdraw() external nonReentrant {
                                    uint256 withdrawAmount = _completeWithdraw();
                                    require(withdrawAmount > 0, "R28");
                                    lastQueuedWithdrawAmount = uint128(
                                        uint256(lastQueuedWithdrawAmount) - withdrawAmount
                                    );
                                    IERC20(vaultParams.asset).safeTransfer(msg.sender, withdrawAmount);
                                }
                                /************************************************
                                 *  VAULT OPERATIONS
                                 ***********************************************/
                                /**
                                 * @notice Stakes a users vault shares
                                 * @param numShares is the number of shares to stake
                                 */
                                function stake(uint256 numShares) external nonReentrant {
                                    address _liquidityGauge = liquidityGauge;
                                    require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit
                                    require(numShares > 0);
                                    uint256 heldByAccount = balanceOf(msg.sender);
                                    if (heldByAccount < numShares) {
                                        _redeem(numShares - heldByAccount, false);
                                    }
                                    _transfer(msg.sender, address(this), numShares);
                                    _approve(address(this), _liquidityGauge, numShares);
                                    ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);
                                }
                                /**
                                 * @notice Rolls the vault's funds into a new loan + long option position.
                                 */
                                function rollToNextRound() external onlyKeeper nonReentrant {
                                    vaultState.lastLockedAmount = uint104(vaultState.lockedAmount);
                                    (uint256 lockedBalance, uint256 queuedWithdrawAmount) =
                                        _rollToNextRound();
                                    lastQueuedWithdrawAmount = queuedWithdrawAmount;
                                    uint256 newQueuedWithdrawShares =
                                        uint256(vaultState.queuedWithdrawShares) +
                                            currentQueuedWithdrawShares;
                                    ShareMath.assertUint128(newQueuedWithdrawShares);
                                    vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);
                                    currentQueuedWithdrawShares = 0;
                                    ShareMath.assertUint104(lockedBalance);
                                    vaultState.lockedAmount = uint104(lockedBalance);
                                    vaultState.optionsBoughtInRound = 0;
                                    uint256 loanAllocation = allocationState.loanAllocation;
                                    for (uint256 i = 0; i < borrowers.length; i++) {
                                        // Amount to lending = total USD loan allocation * weight of current borrower / total weight of all borrowers
                                        uint256 amtToLendToBorrower =
                                            (loanAllocation *
                                                borrowerWeights[borrowers[i]].borrowerWeight) /
                                                totalBorrowerWeight;
                                        IRibbonLend lendPool = IRibbonLend(borrowers[i]);
                                        uint256 currLendingPoolBalance = _lendingPoolBalance(lendPool);
                                        // If we need to decrease loan allocation, exit Ribbon Lend Pool, otherwise allocate to pool
                                        if (currLendingPoolBalance > amtToLendToBorrower) {
                                            lendPool.redeemCurrency(
                                                currLendingPoolBalance - amtToLendToBorrower
                                            );
                                        } else if (amtToLendToBorrower > currLendingPoolBalance) {
                                            IERC20(vaultParams.asset).safeApprove(
                                                borrowers[i],
                                                amtToLendToBorrower - currLendingPoolBalance
                                            );
                                            lendPool.provide(
                                                amtToLendToBorrower - currLendingPoolBalance,
                                                address(0)
                                            );
                                        }
                                    }
                                }
                                /**
                                 * @notice Buys the option by transferring premiums to option seller
                                 */
                                function buyOption() external onlyKeeper {
                                    require(
                                        vaultState.optionsBoughtInRound == 0 ||
                                            block.timestamp >=
                                            uint256(vaultState.lastOptionPurchaseTime) +
                                                allocationState.currentOptionPurchaseFreq,
                                        "R34"
                                    );
                                    uint256 optionAllocation =
                                        allocationState.optionAllocation /
                                            (uint256(allocationState.currentLoanTermLength) /
                                                allocationState.currentOptionPurchaseFreq);
                                    vaultState.optionsBoughtInRound += uint128(optionAllocation);
                                    vaultState.lastOptionPurchaseTime = uint64(
                                        block.timestamp - (block.timestamp % (24 hours)) + (8 hours)
                                    );
                                    IERC20(vaultParams.asset).safeTransfer(optionSeller, optionAllocation);
                                    emit PurchaseOption(optionAllocation, optionSeller);
                                }
                                /**
                                 * @notice Pays option yield if option is ITM
                                 * `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                                 * over the EIP712-formatted function arguments
                                 * @param amount is the amount of yield to pay
                                 * @param deadline must be a timestamp in the future
                                 * @param v is a valid signature
                                 * @param r is a valid signature
                                 * @param s is a valid signature
                                 */
                                function payOptionYield(
                                    uint256 amount,
                                    uint256 deadline,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external onlyOptionSeller {
                                    // Sign for transfer approval
                                    IERC20Permit(vaultParams.asset).permit(
                                        msg.sender,
                                        address(this),
                                        amount,
                                        deadline,
                                        v,
                                        r,
                                        s
                                    );
                                    // Pay option yields to contract
                                    _payOptionYield(amount);
                                }
                                /**
                                 * @notice Pays option yield if option is ITM
                                 * @param amount is the amount of yield to pay
                                 */
                                function payOptionYield(uint256 amount) external onlyOptionSeller {
                                    // Pay option yields to contract
                                    _payOptionYield(amount);
                                }
                                /**
                                 * @notice Recovery function that returns an ERC20 token to the recipient
                                 * @param token is the ERC20 token to recover from the vault
                                 * @param recipient is the recipient of the recovered tokens
                                 */
                                function recoverTokens(address token, address recipient)
                                    external
                                    onlyOwner
                                {
                                    require(token != vaultParams.asset, "R35");
                                    require(token != address(this), "R36");
                                    require(recipient != address(this), "R37");
                                    IERC20(token).safeTransfer(
                                        recipient,
                                        IERC20(token).balanceOf(address(this))
                                    );
                                }
                                /**
                                 * @notice pause a user's vault position
                                 */
                                function pausePosition() external {
                                    address _vaultPauserAddress = vaultPauser;
                                    require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit
                                    _redeem(0, true);
                                    uint256 heldByAccount = balanceOf(msg.sender);
                                    _approve(msg.sender, _vaultPauserAddress, heldByAccount);
                                    IVaultPauser(_vaultPauserAddress).pausePosition(
                                        msg.sender,
                                        heldByAccount
                                    );
                                }
                                /**
                                 * @notice Helper function that performs most administrative tasks
                                 * such as minting new shares, getting vault fees, etc.
                                 * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size
                                 * @return queuedWithdrawAmount is the new queued withdraw amount for this round
                                 */
                                function _rollToNextRound()
                                    internal
                                    returns (uint256 lockedBalance, uint256 queuedWithdrawAmount)
                                {
                                    require(
                                        block.timestamp >=
                                            uint256(vaultState.lastEpochTime) +
                                                allocationState.currentLoanTermLength,
                                        "R39"
                                    );
                                    address recipient = feeRecipient;
                                    uint256 mintShares;
                                    uint256 performanceFeeInAsset;
                                    uint256 totalVaultFee;
                                    {
                                        uint256 newPricePerShare;
                                        (
                                            lockedBalance,
                                            queuedWithdrawAmount,
                                            newPricePerShare,
                                            mintShares,
                                            performanceFeeInAsset,
                                            totalVaultFee
                                        ) = VaultLifecycleEarn.rollover(
                                            vaultState,
                                            VaultLifecycleEarn.RolloverParams(
                                                vaultParams.decimals,
                                                totalBalance(),
                                                totalSupply(),
                                                lastQueuedWithdrawAmount,
                                                performanceFee,
                                                managementFee,
                                                currentQueuedWithdrawShares
                                            )
                                        );
                                        // Finalize the pricePerShare at the end of the round
                                        uint256 currentRound = vaultState.round;
                                        roundPricePerShare[currentRound] = newPricePerShare;
                                        emit CollectVaultFees(
                                            performanceFeeInAsset,
                                            totalVaultFee,
                                            currentRound,
                                            recipient
                                        );
                                        vaultState.totalPending = 0;
                                        vaultState.round = uint16(currentRound + 1);
                                        vaultState.lastEpochTime = uint64(
                                            block.timestamp - (block.timestamp % (24 hours)) + (8 hours)
                                        );
                                    }
                                    _mint(address(this), mintShares);
                                    if (totalVaultFee > 0) {
                                        IERC20(vaultParams.asset).safeTransfer(recipient, totalVaultFee);
                                    }
                                    _updateAllocationState(lockedBalance);
                                    _commitBorrowerBasket();
                                    return (lockedBalance, queuedWithdrawAmount);
                                }
                                /**
                                 * @notice Helper function that transfers funds from option
                                 * seller
                                 * @param amount is the amount of yield to pay
                                 */
                                function _payOptionYield(uint256 amount) internal {
                                    IERC20(vaultParams.asset).safeTransferFrom(
                                        msg.sender,
                                        address(this),
                                        amount
                                    );
                                    uint256 optionAllocation =
                                        allocationState.optionAllocation /
                                            (uint256(allocationState.currentLoanTermLength) /
                                                allocationState.currentOptionPurchaseFreq);
                                    emit PayOptionYield(
                                        amount,
                                        amount > optionAllocation ? amount - optionAllocation : 0,
                                        msg.sender
                                    );
                                }
                                /**
                                 * @notice Helper function that updates allocation state
                                 * such as loan term length, option purchase frequency, loan / option
                                 * allocation split, etc.
                                 * @param lockedBalance is the locked balance for newest epoch
                                 */
                                function _updateAllocationState(uint256 lockedBalance) internal {
                                    Vault.AllocationState memory _allocationState = allocationState;
                                    // Set next loan term length
                                    if (_allocationState.nextLoanTermLength != 0) {
                                        uint256 tmpManagementFee = managementFee;
                                        managementFee =
                                            (tmpManagementFee * _allocationState.nextLoanTermLength) /
                                            _allocationState.currentLoanTermLength;
                                        allocationState.currentLoanTermLength = _allocationState
                                            .nextLoanTermLength;
                                        allocationState.nextLoanTermLength = 0;
                                        emit ManagementFeeSet(tmpManagementFee, managementFee);
                                    }
                                    // Set next option purchase frequency
                                    if (_allocationState.nextOptionPurchaseFreq != 0) {
                                        allocationState.currentOptionPurchaseFreq = _allocationState
                                            .nextOptionPurchaseFreq;
                                        allocationState.nextOptionPurchaseFreq = 0;
                                    }
                                    // Set next loan allocation from vault in USD
                                    allocationState.loanAllocation =
                                        (uint256(_allocationState.loanAllocationPCT) * lockedBalance) /
                                        TOTAL_PCT;
                                    // Set next option allocation from vault per purchase in USD
                                    allocationState.optionAllocation =
                                        (uint256(_allocationState.optionAllocationPCT) * lockedBalance) /
                                        TOTAL_PCT;
                                }
                                /**
                                 * @notice Helper function to update basket of borrowers
                                 * @param pendingBorrowers is the array of borrowers to add
                                 * @param pendingBorrowWeights is the array of corresponding borrow weights for the borrower
                                 */
                                function _updateBorrowerBasket(
                                    address[] calldata pendingBorrowers,
                                    uint128[] calldata pendingBorrowWeights
                                ) internal {
                                    uint256 borrowerArrLen = pendingBorrowers.length;
                                    require(borrowerArrLen == pendingBorrowWeights.length, "R40");
                                    // Set current pending changes to basket of borrowers
                                    for (uint256 i = 0; i < borrowerArrLen; i++) {
                                        if (pendingBorrowers[i] == address(0)) {
                                            continue;
                                        }
                                        // Borrower does not exist
                                        if (!borrowerWeights[pendingBorrowers[i]].exists) {
                                            borrowers.push(pendingBorrowers[i]);
                                            borrowerWeights[pendingBorrowers[i]].exists = true;
                                        }
                                        // Set pending borrower weight
                                        borrowerWeights[pendingBorrowers[i]]
                                            .pendingBorrowerWeight = pendingBorrowWeights[i];
                                    }
                                    emit BorrowerBasketUpdated(pendingBorrowers, pendingBorrowWeights);
                                }
                                /**
                                 * @notice Helper function that commits borrower basket
                                 */
                                function _commitBorrowerBasket() internal {
                                    require(block.timestamp >= (lastBorrowerBasketChange + 3 days), "R10");
                                    // Set current pending changes to basket of borrowers
                                    for (uint256 i = 0; i < borrowers.length; i++) {
                                        uint128 borrowWeight = borrowerWeights[borrowers[i]].borrowerWeight;
                                        uint128 pendingBorrowWeight =
                                            borrowerWeights[borrowers[i]].pendingBorrowerWeight;
                                        // Set borrower weight to pending borrower weight
                                        if (borrowWeight != pendingBorrowWeight) {
                                            borrowerWeights[borrowers[i]]
                                                .borrowerWeight = pendingBorrowWeight;
                                            // Update total borrowing weight
                                            totalBorrowerWeight += pendingBorrowWeight;
                                            totalBorrowerWeight -= borrowWeight;
                                        }
                                    }
                                    emit CommitBorrowerBasket(totalBorrowerWeight);
                                }
                                /************************************************
                                 *  GETTERS
                                 ***********************************************/
                                /**
                                 * @notice Returns the Ribbon Earn vault balance in a Ribbon Lend Pool
                                 * @param lendPool is the Ribbon Lend pool
                                 * @return the amount of `asset` deposited into the lend pool
                                 */
                                function _lendingPoolBalance(IRibbonLend lendPool)
                                    internal
                                    view
                                    returns (uint256)
                                {
                                    // Check for Non-RibbonLend Borrowers
                                    if (
                                        address(lendPool) == 0xA1614eC01d13E04522ED0b085C7a178ED9E99bc9 ||
                                        address(lendPool) == 0x44C8e19Bd59A8EA895fFf60DBB4e762028f2fb71
                                    ) {
                                        return 0;
                                    }
                                    // Current exchange rate is 18-digits decimal
                                    return
                                        (lendPool.balanceOf(address(this)) *
                                            lendPool.getCurrentExchangeRate()) / 10**18;
                                }
                                /**
                                 * @notice Returns the asset balance held on the vault for the account
                                 * @param account is the address to lookup balance for
                                 * @return the amount of `asset` custodied by the vault for the user
                                 */
                                function accountVaultBalance(address account)
                                    external
                                    view
                                    returns (uint256)
                                {
                                    uint256 _decimals = vaultParams.decimals;
                                    uint256 assetPerShare =
                                        ShareMath.pricePerShare(
                                            totalSupply(),
                                            totalBalance(),
                                            vaultState.totalPending,
                                            _decimals
                                        );
                                    return
                                        ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);
                                }
                                /**
                                 * @notice Getter for returning the account's share balance including unredeemed shares
                                 * @param account is the account to lookup share balance for
                                 * @return the share balance
                                 */
                                function shares(address account) public view returns (uint256) {
                                    (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);
                                    return heldByAccount + heldByVault;
                                }
                                /**
                                 * @notice Getter for returning the account's share balance split between account and vault holdings
                                 * @param account is the account to lookup share balance for
                                 * @return heldByAccount is the shares held by account
                                 * @return heldByVault is the shares held on the vault (unredeemedShares)
                                 */
                                function shareBalances(address account)
                                    public
                                    view
                                    returns (uint256 heldByAccount, uint256 heldByVault)
                                {
                                    Vault.DepositReceipt memory depositReceipt = depositReceipts[account];
                                    if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {
                                        return (balanceOf(account), 0);
                                    }
                                    uint256 unredeemedShares =
                                        depositReceipt.getSharesFromReceipt(
                                            vaultState.round,
                                            roundPricePerShare[depositReceipt.round],
                                            vaultParams.decimals
                                        );
                                    return (balanceOf(account), unredeemedShares);
                                }
                                /**
                                 * @notice The price of a unit of share denominated in the `asset`
                                 */
                                function pricePerShare() external view returns (uint256) {
                                    return
                                        ShareMath.pricePerShare(
                                            totalSupply(),
                                            totalBalance(),
                                            vaultState.totalPending,
                                            vaultParams.decimals
                                        );
                                }
                                /**
                                 * @notice Returns the vault's total balance, including the amounts lent out
                                 * @return total balance of the vault, including the amounts locked in third party protocols
                                 */
                                function totalBalance() public view returns (uint256) {
                                    // Does not include funds allocated for options purchases
                                    // Includes funds set aside in vault that guarantee base yield
                                    uint256 totalBalance =
                                        IERC20(vaultParams.asset).balanceOf(address(this));
                                    for (uint256 i = 0; i < borrowers.length; i++) {
                                        totalBalance += _lendingPoolBalance(IRibbonLend(borrowers[i]));
                                    }
                                    return totalBalance;
                                }
                                /**
                                 * @notice Returns the token decimals
                                 */
                                function decimals() public view override returns (uint8) {
                                    return vaultParams.decimals;
                                }
                                function cap() external view returns (uint256) {
                                    return vaultParams.cap;
                                }
                                function totalPending() external view returns (uint256) {
                                    return vaultState.totalPending;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Interface of the ERC20 standard as defined in the EIP.
                             */
                            interface IERC20 {
                                /**
                                 * @dev Returns the amount of tokens in existence.
                                 */
                                function totalSupply() external view returns (uint256);
                                /**
                                 * @dev Returns the amount of tokens owned by `account`.
                                 */
                                function balanceOf(address account) external view returns (uint256);
                                /**
                                 * @dev Moves `amount` tokens from the caller's account to `recipient`.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * Emits a {Transfer} event.
                                 */
                                function transfer(address recipient, uint256 amount) external returns (bool);
                                /**
                                 * @dev Returns the remaining number of tokens that `spender` will be
                                 * allowed to spend on behalf of `owner` through {transferFrom}. This is
                                 * zero by default.
                                 *
                                 * This value changes when {approve} or {transferFrom} are called.
                                 */
                                function allowance(address owner, address spender) external view returns (uint256);
                                /**
                                 * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * IMPORTANT: Beware that changing an allowance with this method brings the risk
                                 * that someone may use both the old and the new allowance by unfortunate
                                 * transaction ordering. One possible solution to mitigate this race
                                 * condition is to first reduce the spender's allowance to 0 and set the
                                 * desired value afterwards:
                                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                                 *
                                 * Emits an {Approval} event.
                                 */
                                function approve(address spender, uint256 amount) external returns (bool);
                                /**
                                 * @dev Moves `amount` tokens from `sender` to `recipient` using the
                                 * allowance mechanism. `amount` is then deducted from the caller's
                                 * allowance.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * Emits a {Transfer} event.
                                 */
                                function transferFrom(
                                    address sender,
                                    address recipient,
                                    uint256 amount
                                ) external returns (bool);
                                /**
                                 * @dev Emitted when `value` tokens are moved from one account (`from`) to
                                 * another (`to`).
                                 *
                                 * Note that `value` may be zero.
                                 */
                                event Transfer(address indexed from, address indexed to, uint256 value);
                                /**
                                 * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                                 * a call to {approve}. `value` is the new allowance.
                                 */
                                event Approval(address indexed owner, address indexed spender, uint256 value);
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                            interface IERC20Detailed is IERC20 {
                                function decimals() external view returns (uint8);
                                function symbol() external view returns (string calldata);
                                function name() external view returns (string calldata);
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                             * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                             *
                             * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                             * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                             * need to send a transaction, and thus is not required to hold Ether at all.
                             */
                            interface IERC20Permit {
                                /**
                                 * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                                 * given ``owner``'s signed approval.
                                 *
                                 * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                                 * ordering also apply here.
                                 *
                                 * Emits an {Approval} event.
                                 *
                                 * Requirements:
                                 *
                                 * - `spender` cannot be the zero address.
                                 * - `deadline` must be a timestamp in the future.
                                 * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                                 * over the EIP712-formatted function arguments.
                                 * - the signature must use ``owner``'s current nonce (see {nonces}).
                                 *
                                 * For more information on the signature format, see the
                                 * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                                 * section].
                                 */
                                function permit(
                                    address owner,
                                    address spender,
                                    uint256 value,
                                    uint256 deadline,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external;
                                /**
                                 * @dev Returns the current nonce for `owner`. This value must be
                                 * included whenever a signature is generated for {permit}.
                                 *
                                 * Every successful call to {permit} increases ``owner``'s nonce by one. This
                                 * prevents a signature from being used multiple times.
                                 */
                                function nonces(address owner) external view returns (uint256);
                                /**
                                 * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                                 */
                                // solhint-disable-next-line func-name-mixedcase
                                function DOMAIN_SEPARATOR() external view returns (bytes32);
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                             * checks.
                             *
                             * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                             * easily result in undesired exploitation or bugs, since developers usually
                             * assume that overflows raise errors. `SafeCast` restores this intuition by
                             * reverting the transaction when such an operation overflows.
                             *
                             * Using this library instead of the unchecked operations eliminates an entire
                             * class of bugs, so it's recommended to use it always.
                             *
                             * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                             * all math on `uint256` and `int256` and then downcasting.
                             */
                            library SafeCast {
                                /**
                                 * @dev Returns the downcasted uint224 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint224).
                                 *
                                 * Counterpart to Solidity's `uint224` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 224 bits
                                 */
                                function toUint224(uint256 value) internal pure returns (uint224) {
                                    require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                                    return uint224(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint128 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint128).
                                 *
                                 * Counterpart to Solidity's `uint128` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 128 bits
                                 */
                                function toUint128(uint256 value) internal pure returns (uint128) {
                                    require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                                    return uint128(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint96 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint96).
                                 *
                                 * Counterpart to Solidity's `uint96` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 96 bits
                                 */
                                function toUint96(uint256 value) internal pure returns (uint96) {
                                    require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                                    return uint96(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint64 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint64).
                                 *
                                 * Counterpart to Solidity's `uint64` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 64 bits
                                 */
                                function toUint64(uint256 value) internal pure returns (uint64) {
                                    require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                                    return uint64(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint32 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint32).
                                 *
                                 * Counterpart to Solidity's `uint32` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 32 bits
                                 */
                                function toUint32(uint256 value) internal pure returns (uint32) {
                                    require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                                    return uint32(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint16 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint16).
                                 *
                                 * Counterpart to Solidity's `uint16` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 16 bits
                                 */
                                function toUint16(uint256 value) internal pure returns (uint16) {
                                    require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                                    return uint16(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint8 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint8).
                                 *
                                 * Counterpart to Solidity's `uint8` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 8 bits.
                                 */
                                function toUint8(uint256 value) internal pure returns (uint8) {
                                    require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                                    return uint8(value);
                                }
                                /**
                                 * @dev Converts a signed int256 into an unsigned uint256.
                                 *
                                 * Requirements:
                                 *
                                 * - input must be greater than or equal to 0.
                                 */
                                function toUint256(int256 value) internal pure returns (uint256) {
                                    require(value >= 0, "SafeCast: value must be positive");
                                    return uint256(value);
                                }
                                /**
                                 * @dev Returns the downcasted int128 from int256, reverting on
                                 * overflow (when the input is less than smallest int128 or
                                 * greater than largest int128).
                                 *
                                 * Counterpart to Solidity's `int128` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 128 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt128(int256 value) internal pure returns (int128) {
                                    require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
                                    return int128(value);
                                }
                                /**
                                 * @dev Returns the downcasted int64 from int256, reverting on
                                 * overflow (when the input is less than smallest int64 or
                                 * greater than largest int64).
                                 *
                                 * Counterpart to Solidity's `int64` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 64 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt64(int256 value) internal pure returns (int64) {
                                    require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
                                    return int64(value);
                                }
                                /**
                                 * @dev Returns the downcasted int32 from int256, reverting on
                                 * overflow (when the input is less than smallest int32 or
                                 * greater than largest int32).
                                 *
                                 * Counterpart to Solidity's `int32` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 32 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt32(int256 value) internal pure returns (int32) {
                                    require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
                                    return int32(value);
                                }
                                /**
                                 * @dev Returns the downcasted int16 from int256, reverting on
                                 * overflow (when the input is less than smallest int16 or
                                 * greater than largest int16).
                                 *
                                 * Counterpart to Solidity's `int16` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 16 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt16(int256 value) internal pure returns (int16) {
                                    require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
                                    return int16(value);
                                }
                                /**
                                 * @dev Returns the downcasted int8 from int256, reverting on
                                 * overflow (when the input is less than smallest int8 or
                                 * greater than largest int8).
                                 *
                                 * Counterpart to Solidity's `int8` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 8 bits.
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt8(int256 value) internal pure returns (int8) {
                                    require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
                                    return int8(value);
                                }
                                /**
                                 * @dev Converts an unsigned uint256 into a signed int256.
                                 *
                                 * Requirements:
                                 *
                                 * - input must be less than or equal to maxInt256.
                                 */
                                function toInt256(uint256 value) internal pure returns (int256) {
                                    // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                                    require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                                    return int256(value);
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            import "../proxy/utils/Initializable.sol";
                            /**
                             * @dev Contract module that helps prevent reentrant calls to a function.
                             *
                             * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                             * available, which can be applied to functions to make sure there are no nested
                             * (reentrant) calls to them.
                             *
                             * Note that because there is a single `nonReentrant` guard, functions marked as
                             * `nonReentrant` may not call one another. This can be worked around by making
                             * those functions `private`, and then adding `external` `nonReentrant` entry
                             * points to them.
                             *
                             * TIP: If you would like to learn more about reentrancy and alternative ways
                             * to protect against it, check out our blog post
                             * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                             */
                            abstract contract ReentrancyGuardUpgradeable is Initializable {
                                // Booleans are more expensive than uint256 or any type that takes up a full
                                // word because each write operation emits an extra SLOAD to first read the
                                // slot's contents, replace the bits taken up by the boolean, and then write
                                // back. This is the compiler's defense against contract upgrades and
                                // pointer aliasing, and it cannot be disabled.
                                // The values being non-zero value makes deployment a bit more expensive,
                                // but in exchange the refund on every call to nonReentrant will be lower in
                                // amount. Since refunds are capped to a percentage of the total
                                // transaction's gas, it is best to keep them low in cases like this one, to
                                // increase the likelihood of the full refund coming into effect.
                                uint256 private constant _NOT_ENTERED = 1;
                                uint256 private constant _ENTERED = 2;
                                uint256 private _status;
                                function __ReentrancyGuard_init() internal initializer {
                                    __ReentrancyGuard_init_unchained();
                                }
                                function __ReentrancyGuard_init_unchained() internal initializer {
                                    _status = _NOT_ENTERED;
                                }
                                /**
                                 * @dev Prevents a contract from calling itself, directly or indirectly.
                                 * Calling a `nonReentrant` function from another `nonReentrant`
                                 * function is not supported. It is possible to prevent this from happening
                                 * by making the `nonReentrant` function external, and make it call a
                                 * `private` function that does the actual work.
                                 */
                                modifier nonReentrant() {
                                    // On the first call to nonReentrant, _notEntered will be true
                                    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                                    // Any calls to nonReentrant after this point will fail
                                    _status = _ENTERED;
                                    _;
                                    // By storing the original value once again, a refund is triggered (see
                                    // https://eips.ethereum.org/EIPS/eip-2200)
                                    _status = _NOT_ENTERED;
                                }
                                uint256[49] private __gap;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            import "../utils/ContextUpgradeable.sol";
                            import "../proxy/utils/Initializable.sol";
                            /**
                             * @dev Contract module which provides a basic access control mechanism, where
                             * there is an account (an owner) that can be granted exclusive access to
                             * specific functions.
                             *
                             * By default, the owner account will be the one that deploys the contract. This
                             * can later be changed with {transferOwnership}.
                             *
                             * This module is used through inheritance. It will make available the modifier
                             * `onlyOwner`, which can be applied to your functions to restrict their use to
                             * the owner.
                             */
                            abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                                address private _owner;
                                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                                /**
                                 * @dev Initializes the contract setting the deployer as the initial owner.
                                 */
                                function __Ownable_init() internal initializer {
                                    __Context_init_unchained();
                                    __Ownable_init_unchained();
                                }
                                function __Ownable_init_unchained() internal initializer {
                                    _setOwner(_msgSender());
                                }
                                /**
                                 * @dev Returns the address of the current owner.
                                 */
                                function owner() public view virtual returns (address) {
                                    return _owner;
                                }
                                /**
                                 * @dev Throws if called by any account other than the owner.
                                 */
                                modifier onlyOwner() {
                                    require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                    _;
                                }
                                /**
                                 * @dev Leaves the contract without owner. It will not be possible to call
                                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                                 *
                                 * NOTE: Renouncing ownership will leave the contract without an owner,
                                 * thereby removing any functionality that is only available to the owner.
                                 */
                                function renounceOwnership() public virtual onlyOwner {
                                    _setOwner(address(0));
                                }
                                /**
                                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                                 * Can only be called by the current owner.
                                 */
                                function transferOwnership(address newOwner) public virtual onlyOwner {
                                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                                    _setOwner(newOwner);
                                }
                                function _setOwner(address newOwner) private {
                                    address oldOwner = _owner;
                                    _owner = newOwner;
                                    emit OwnershipTransferred(oldOwner, newOwner);
                                }
                                uint256[49] private __gap;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            import "./IERC20Upgradeable.sol";
                            import "./extensions/IERC20MetadataUpgradeable.sol";
                            import "../../utils/ContextUpgradeable.sol";
                            import "../../proxy/utils/Initializable.sol";
                            /**
                             * @dev Implementation of the {IERC20} interface.
                             *
                             * This implementation is agnostic to the way tokens are created. This means
                             * that a supply mechanism has to be added in a derived contract using {_mint}.
                             * For a generic mechanism see {ERC20PresetMinterPauser}.
                             *
                             * TIP: For a detailed writeup see our guide
                             * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                             * to implement supply mechanisms].
                             *
                             * We have followed general OpenZeppelin Contracts guidelines: functions revert
                             * instead returning `false` on failure. This behavior is nonetheless
                             * conventional and does not conflict with the expectations of ERC20
                             * applications.
                             *
                             * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                             * This allows applications to reconstruct the allowance for all accounts just
                             * by listening to said events. Other implementations of the EIP may not emit
                             * these events, as it isn't required by the specification.
                             *
                             * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                             * functions have been added to mitigate the well-known issues around setting
                             * allowances. See {IERC20-approve}.
                             */
                            contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
                                mapping(address => uint256) private _balances;
                                mapping(address => mapping(address => uint256)) private _allowances;
                                uint256 private _totalSupply;
                                string private _name;
                                string private _symbol;
                                /**
                                 * @dev Sets the values for {name} and {symbol}.
                                 *
                                 * The default value of {decimals} is 18. To select a different value for
                                 * {decimals} you should overload it.
                                 *
                                 * All two of these values are immutable: they can only be set once during
                                 * construction.
                                 */
                                function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
                                    __Context_init_unchained();
                                    __ERC20_init_unchained(name_, symbol_);
                                }
                                function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
                                    _name = name_;
                                    _symbol = symbol_;
                                }
                                /**
                                 * @dev Returns the name of the token.
                                 */
                                function name() public view virtual override returns (string memory) {
                                    return _name;
                                }
                                /**
                                 * @dev Returns the symbol of the token, usually a shorter version of the
                                 * name.
                                 */
                                function symbol() public view virtual override returns (string memory) {
                                    return _symbol;
                                }
                                /**
                                 * @dev Returns the number of decimals used to get its user representation.
                                 * For example, if `decimals` equals `2`, a balance of `505` tokens should
                                 * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                                 *
                                 * Tokens usually opt for a value of 18, imitating the relationship between
                                 * Ether and Wei. This is the value {ERC20} uses, unless this function is
                                 * overridden;
                                 *
                                 * NOTE: This information is only used for _display_ purposes: it in
                                 * no way affects any of the arithmetic of the contract, including
                                 * {IERC20-balanceOf} and {IERC20-transfer}.
                                 */
                                function decimals() public view virtual override returns (uint8) {
                                    return 18;
                                }
                                /**
                                 * @dev See {IERC20-totalSupply}.
                                 */
                                function totalSupply() public view virtual override returns (uint256) {
                                    return _totalSupply;
                                }
                                /**
                                 * @dev See {IERC20-balanceOf}.
                                 */
                                function balanceOf(address account) public view virtual override returns (uint256) {
                                    return _balances[account];
                                }
                                /**
                                 * @dev See {IERC20-transfer}.
                                 *
                                 * Requirements:
                                 *
                                 * - `recipient` cannot be the zero address.
                                 * - the caller must have a balance of at least `amount`.
                                 */
                                function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                                    _transfer(_msgSender(), recipient, amount);
                                    return true;
                                }
                                /**
                                 * @dev See {IERC20-allowance}.
                                 */
                                function allowance(address owner, address spender) public view virtual override returns (uint256) {
                                    return _allowances[owner][spender];
                                }
                                /**
                                 * @dev See {IERC20-approve}.
                                 *
                                 * Requirements:
                                 *
                                 * - `spender` cannot be the zero address.
                                 */
                                function approve(address spender, uint256 amount) public virtual override returns (bool) {
                                    _approve(_msgSender(), spender, amount);
                                    return true;
                                }
                                /**
                                 * @dev See {IERC20-transferFrom}.
                                 *
                                 * Emits an {Approval} event indicating the updated allowance. This is not
                                 * required by the EIP. See the note at the beginning of {ERC20}.
                                 *
                                 * Requirements:
                                 *
                                 * - `sender` and `recipient` cannot be the zero address.
                                 * - `sender` must have a balance of at least `amount`.
                                 * - the caller must have allowance for ``sender``'s tokens of at least
                                 * `amount`.
                                 */
                                function transferFrom(
                                    address sender,
                                    address recipient,
                                    uint256 amount
                                ) public virtual override returns (bool) {
                                    _transfer(sender, recipient, amount);
                                    uint256 currentAllowance = _allowances[sender][_msgSender()];
                                    require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
                                    unchecked {
                                        _approve(sender, _msgSender(), currentAllowance - amount);
                                    }
                                    return true;
                                }
                                /**
                                 * @dev Atomically increases the allowance granted to `spender` by the caller.
                                 *
                                 * This is an alternative to {approve} that can be used as a mitigation for
                                 * problems described in {IERC20-approve}.
                                 *
                                 * Emits an {Approval} event indicating the updated allowance.
                                 *
                                 * Requirements:
                                 *
                                 * - `spender` cannot be the zero address.
                                 */
                                function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                                    _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                                    return true;
                                }
                                /**
                                 * @dev Atomically decreases the allowance granted to `spender` by the caller.
                                 *
                                 * This is an alternative to {approve} that can be used as a mitigation for
                                 * problems described in {IERC20-approve}.
                                 *
                                 * Emits an {Approval} event indicating the updated allowance.
                                 *
                                 * Requirements:
                                 *
                                 * - `spender` cannot be the zero address.
                                 * - `spender` must have allowance for the caller of at least
                                 * `subtractedValue`.
                                 */
                                function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                                    uint256 currentAllowance = _allowances[_msgSender()][spender];
                                    require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                                    unchecked {
                                        _approve(_msgSender(), spender, currentAllowance - subtractedValue);
                                    }
                                    return true;
                                }
                                /**
                                 * @dev Moves `amount` of tokens from `sender` to `recipient`.
                                 *
                                 * This internal function is equivalent to {transfer}, and can be used to
                                 * e.g. implement automatic token fees, slashing mechanisms, etc.
                                 *
                                 * Emits a {Transfer} event.
                                 *
                                 * Requirements:
                                 *
                                 * - `sender` cannot be the zero address.
                                 * - `recipient` cannot be the zero address.
                                 * - `sender` must have a balance of at least `amount`.
                                 */
                                function _transfer(
                                    address sender,
                                    address recipient,
                                    uint256 amount
                                ) internal virtual {
                                    require(sender != address(0), "ERC20: transfer from the zero address");
                                    require(recipient != address(0), "ERC20: transfer to the zero address");
                                    _beforeTokenTransfer(sender, recipient, amount);
                                    uint256 senderBalance = _balances[sender];
                                    require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
                                    unchecked {
                                        _balances[sender] = senderBalance - amount;
                                    }
                                    _balances[recipient] += amount;
                                    emit Transfer(sender, recipient, amount);
                                    _afterTokenTransfer(sender, recipient, amount);
                                }
                                /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                                 * the total supply.
                                 *
                                 * Emits a {Transfer} event with `from` set to the zero address.
                                 *
                                 * Requirements:
                                 *
                                 * - `account` cannot be the zero address.
                                 */
                                function _mint(address account, uint256 amount) internal virtual {
                                    require(account != address(0), "ERC20: mint to the zero address");
                                    _beforeTokenTransfer(address(0), account, amount);
                                    _totalSupply += amount;
                                    _balances[account] += amount;
                                    emit Transfer(address(0), account, amount);
                                    _afterTokenTransfer(address(0), account, amount);
                                }
                                /**
                                 * @dev Destroys `amount` tokens from `account`, reducing the
                                 * total supply.
                                 *
                                 * Emits a {Transfer} event with `to` set to the zero address.
                                 *
                                 * Requirements:
                                 *
                                 * - `account` cannot be the zero address.
                                 * - `account` must have at least `amount` tokens.
                                 */
                                function _burn(address account, uint256 amount) internal virtual {
                                    require(account != address(0), "ERC20: burn from the zero address");
                                    _beforeTokenTransfer(account, address(0), amount);
                                    uint256 accountBalance = _balances[account];
                                    require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                                    unchecked {
                                        _balances[account] = accountBalance - amount;
                                    }
                                    _totalSupply -= amount;
                                    emit Transfer(account, address(0), amount);
                                    _afterTokenTransfer(account, address(0), amount);
                                }
                                /**
                                 * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                                 *
                                 * This internal function is equivalent to `approve`, and can be used to
                                 * e.g. set automatic allowances for certain subsystems, etc.
                                 *
                                 * Emits an {Approval} event.
                                 *
                                 * Requirements:
                                 *
                                 * - `owner` cannot be the zero address.
                                 * - `spender` cannot be the zero address.
                                 */
                                function _approve(
                                    address owner,
                                    address spender,
                                    uint256 amount
                                ) internal virtual {
                                    require(owner != address(0), "ERC20: approve from the zero address");
                                    require(spender != address(0), "ERC20: approve to the zero address");
                                    _allowances[owner][spender] = amount;
                                    emit Approval(owner, spender, amount);
                                }
                                /**
                                 * @dev Hook that is called before any transfer of tokens. This includes
                                 * minting and burning.
                                 *
                                 * Calling conditions:
                                 *
                                 * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                                 * will be transferred to `to`.
                                 * - when `from` is zero, `amount` tokens will be minted for `to`.
                                 * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                                 * - `from` and `to` are never both zero.
                                 *
                                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                                 */
                                function _beforeTokenTransfer(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) internal virtual {}
                                /**
                                 * @dev Hook that is called after any transfer of tokens. This includes
                                 * minting and burning.
                                 *
                                 * Calling conditions:
                                 *
                                 * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                                 * has been transferred to `to`.
                                 * - when `from` is zero, `amount` tokens have been minted for `to`.
                                 * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                                 * - `from` and `to` are never both zero.
                                 *
                                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                                 */
                                function _afterTokenTransfer(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) internal virtual {}
                                uint256[45] private __gap;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            import "../IERC20.sol";
                            import "../../../utils/Address.sol";
                            /**
                             * @title SafeERC20
                             * @dev Wrappers around ERC20 operations that throw on failure (when the token
                             * contract returns false). Tokens that return no value (and instead revert or
                             * throw on failure) are also supported, non-reverting calls are assumed to be
                             * successful.
                             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                             */
                            library SafeERC20 {
                                using Address for address;
                                function safeTransfer(
                                    IERC20 token,
                                    address to,
                                    uint256 value
                                ) internal {
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                                }
                                function safeTransferFrom(
                                    IERC20 token,
                                    address from,
                                    address to,
                                    uint256 value
                                ) internal {
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                                }
                                /**
                                 * @dev Deprecated. This function has issues similar to the ones found in
                                 * {IERC20-approve}, and its usage is discouraged.
                                 *
                                 * Whenever possible, use {safeIncreaseAllowance} and
                                 * {safeDecreaseAllowance} instead.
                                 */
                                function safeApprove(
                                    IERC20 token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    // safeApprove should only be called when setting an initial allowance,
                                    // or when resetting it to zero. To increase and decrease it, use
                                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                                    require(
                                        (value == 0) || (token.allowance(address(this), spender) == 0),
                                        "SafeERC20: approve from non-zero to non-zero allowance"
                                    );
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                                }
                                function safeIncreaseAllowance(
                                    IERC20 token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    uint256 newAllowance = token.allowance(address(this), spender) + value;
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                }
                                function safeDecreaseAllowance(
                                    IERC20 token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    unchecked {
                                        uint256 oldAllowance = token.allowance(address(this), spender);
                                        require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                                        uint256 newAllowance = oldAllowance - value;
                                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                    }
                                }
                                /**
                                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                                 * @param token The token targeted by the call.
                                 * @param data The call data (encoded using abi.encode or one of its variants).
                                 */
                                function _callOptionalReturn(IERC20 token, bytes memory data) private {
                                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                                    // the target address contains contract code and also asserts for success in the low-level call.
                                    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                                    if (returndata.length > 0) {
                                        // Return data is optional
                                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            interface IWETH {
                                function deposit() external payable;
                                function withdraw(uint256) external;
                                function balanceOf(address account) external view returns (uint256);
                                function transfer(address recipient, uint256 amount)
                                    external
                                    returns (bool);
                                function allowance(address owner, address spender)
                                    external
                                    view
                                    returns (uint256);
                                function approve(address spender, uint256 amount) external returns (bool);
                                function transferFrom(
                                    address sender,
                                    address recipient,
                                    uint256 amount
                                ) external returns (bool);
                                function decimals() external view returns (uint256);
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            import {Vault} from "../libraries/Vault.sol";
                            abstract contract RibbonEarnVaultStorageV1 {
                                /// @notice Stores the user's pending deposit for the round
                                mapping(address => Vault.DepositReceipt) public depositReceipts;
                                /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored
                                /// This is used to determine the number of shares to be returned
                                /// to a user with their DepositReceipt.depositAmount
                                mapping(uint256 => uint256) public roundPricePerShare;
                                /// @notice Stores pending user withdrawals
                                mapping(address => Vault.Withdrawal) public withdrawals;
                                /// @notice Vault's parameters like cap, decimals
                                Vault.VaultParams public vaultParams;
                                /// @notice Vault's lifecycle state like round and locked amounts
                                Vault.VaultState public vaultState;
                                /// @notice Vault's state of the allocation between lending and buying options
                                Vault.AllocationState public allocationState;
                                /// @notice Fee recipient for the performance and management fees
                                address public feeRecipient;
                                /// @notice role in charge of weekly vault operations such as rollToNextRound and burnRemainingOTokens
                                // no access to critical vault changes
                                address public keeper;
                                /// @notice borrowers is the array of borrowing addresses (EX: Wintermute, GSR, Alameda, Genesis)
                                address[] public borrowers;
                                /// @notice borrowerMapping is the mapping from borrower to their borrow weight. EX: {Wintermute -> 0.4, GSR -> 0.2}
                                mapping(address => Vault.BorrowerState) public borrowerWeights;
                                /// @notice totalBorrowerWeight is the total borrowing weight
                                uint256 public totalBorrowerWeight;
                                /// @notice lastBorrowerBasketChange is the last time the basket of borrowers was changed (add / remove / set)
                                uint256 public lastBorrowerBasketChange;
                                /// @notice optionSeller is the address of the entity that we will be buying options from (EX: Orbit)
                                address public optionSeller;
                                /// @notice pendingOptionSeller is the pending address of the entity that we will be buying options from (EX: Orbit)
                                address public pendingOptionSeller;
                                /// @notice lastOptionSellerChange is the last time option seller was set
                                uint256 public lastOptionSellerChange;
                                /// @notice Performance fee charged on premiums earned in rollToNextRound. Only charged when there is no loss.
                                uint256 public performanceFee;
                                /// @notice Management fee charged on entire AUM in rollToNextRound. Only charged when there is no loss.
                                uint256 public managementFee;
                                /// @notice Amount locked for scheduled withdrawals last week;
                                uint256 public lastQueuedWithdrawAmount;
                                /// @notice Queued withdraw shares for the current round
                                uint256 public currentQueuedWithdrawShares;
                                /// @notice Vault Pauser Contract for the vault
                                address public vaultPauser;
                                /// @notice LiquidityGauge contract for the vault
                                address public liquidityGauge;
                            }
                            // We are following Compound's method of upgrading new contract implementations
                            // When we need to add new storage variables, we create a new version of RibbonEarnVaultStorage
                            // e.g. RibbonEarnVaultStorage<versionNumber>, so finally it would look like
                            // contract RibbonEarnVaultStorage is RibbonEarnVaultStorageV1, RibbonEarnVaultStorageV2
                            abstract contract RibbonEarnVaultStorage is RibbonEarnVaultStorageV1 {
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            library Vault {
                                /************************************************
                                 *  IMMUTABLES & CONSTANTS
                                 ***********************************************/
                                // Fees are 6-decimal places. For example: 20 * 10**6 = 20%
                                uint256 internal constant FEE_MULTIPLIER = 10**6;
                                // Yield is scaled by 100 (10 ** 2) for PCT.
                                uint256 internal constant YIELD_MULTIPLIER = 10**2;
                                // Placeholder uint value to prevent cold writes
                                uint256 internal constant PLACEHOLDER_UINT = 1;
                                struct VaultParams {
                                    // Token decimals for vault shares
                                    uint8 decimals;
                                    // Asset used in vault
                                    address asset;
                                    // Minimum supply of the vault shares issued, for ETH it's 10**10
                                    uint56 minimumSupply;
                                    // Vault cap
                                    uint104 cap;
                                }
                                struct AllocationState {
                                    // Next Loan Term Length
                                    uint32 nextLoanTermLength;
                                    // Next Option Purchase Frequency
                                    uint32 nextOptionPurchaseFreq;
                                    // Current Loan Term Length
                                    uint32 currentLoanTermLength;
                                    // Current Option Purchase Frequency
                                    uint32 currentOptionPurchaseFreq;
                                    // Current Loan Allocation Percent
                                    uint32 loanAllocationPCT;
                                    // Current Option Purchase Allocation Percent
                                    uint32 optionAllocationPCT;
                                    // Loan Allocation in USD
                                    uint256 loanAllocation;
                                    // Option Purchase Allocation across all purchases
                                    uint256 optionAllocation;
                                }
                                struct VaultState {
                                    // 32 byte slot 1
                                    //  Current round number. `round` represents the number of `period`s elapsed.
                                    uint16 round;
                                    // Amount that is currently locked for the strategy
                                    uint104 lockedAmount;
                                    // Amount that was locked for the strategy in the previous round
                                    // used for calculating performance fee deduction
                                    uint104 lastLockedAmount;
                                    // 32 byte slot 2
                                    // Stores the total tally of how much of `asset` there is
                                    // to be used to mint rTHETA tokens
                                    uint128 totalPending;
                                    // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)
                                    uint128 queuedWithdrawShares;
                                    // Last Loan Allocation Date
                                    uint64 lastEpochTime;
                                    // Last Option Purchase Date
                                    uint64 lastOptionPurchaseTime;
                                    // Amount of options bought in current round
                                    uint128 optionsBoughtInRound;
                                    // DEPRECATED FOR VARIABLE RATE
                                    // Amount of funds returned this round
                                    uint256 amtFundsReturned;
                                }
                                struct DepositReceipt {
                                    // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.
                                    uint16 round;
                                    // Deposit amount, max 20,282,409,603,651 or 20 trillion ETH deposit
                                    uint104 amount;
                                    // Unredeemed shares balance
                                    uint128 unredeemedShares;
                                }
                                struct Withdrawal {
                                    // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.
                                    uint16 round;
                                    // Number of shares withdrawn
                                    uint128 shares;
                                }
                                /**
                                 * @param borrowerWeight is the borrow weight of the borrower
                                 * @param pendingBorrowerWeight is the pending borrow weight
                                 * @param exists is whether the borrower has already been added
                                 */
                                struct BorrowerState {
                                    // Borrower exists
                                    bool exists;
                                    // Borrower weight
                                    uint128 borrowerWeight;
                                    // Borrower weight
                                    uint128 pendingBorrowerWeight;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                            import {Vault} from "./Vault.sol";
                            import {ShareMath} from "./ShareMath.sol";
                            import {IERC20Detailed} from "../interfaces/IERC20Detailed.sol";
                            import {SupportsNonCompliantERC20} from "./SupportsNonCompliantERC20.sol";
                            library VaultLifecycleEarn {
                                using SupportsNonCompliantERC20 for IERC20;
                                /**
                                 * @param decimals is the decimals of the asset
                                 * @param totalBalance is the vaults total balance of the asset
                                 * @param currentShareSupply is the supply of the shares invoked with totalSupply()
                                 * @param lastQueuedWithdrawAmount is the total amount queued for withdrawals
                                 * @param performanceFee is the perf fee percent to charge on premiums
                                 * @param managementFee is the management fee percent to charge on the AUM
                                 * @param currentQueuedWithdrawShares is amount of queued withdrawals from the current round
                                 */
                                struct RolloverParams {
                                    uint256 decimals;
                                    uint256 totalBalance;
                                    uint256 currentShareSupply;
                                    uint256 lastQueuedWithdrawAmount;
                                    uint256 performanceFee;
                                    uint256 managementFee;
                                    uint256 currentQueuedWithdrawShares;
                                }
                                /**
                                 * @notice Calculate the shares to mint, new price per share, and
                                  amount of funds to re-allocate as collateral for the new round
                                 * @param vaultState is the storage variable vaultState passed from RibbonVault
                                 * @param params is the rollover parameters passed to compute the next state
                                 * @return newLockedAmount is the amount of funds to allocate for the new round
                                 * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal
                                 * @return newPricePerShare is the price per share of the new round
                                 * @return mintShares is the amount of shares to mint from deposits
                                 * @return performanceFeeInAsset is the performance fee charged by vault
                                 * @return totalVaultFee is the total amount of fee charged by vault
                                 */
                                function rollover(
                                    Vault.VaultState storage vaultState,
                                    RolloverParams calldata params
                                )
                                    external
                                    view
                                    returns (
                                        uint256 newLockedAmount,
                                        uint256 queuedWithdrawAmount,
                                        uint256 newPricePerShare,
                                        uint256 mintShares,
                                        uint256 performanceFeeInAsset,
                                        uint256 totalVaultFee
                                    )
                                {
                                    uint256 currentBalance = params.totalBalance;
                                    uint256 pendingAmount = vaultState.totalPending;
                                    // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)
                                    uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares;
                                    // Deduct older queued withdraws so we don't charge fees on them
                                    uint256 balanceForVaultFees =
                                        currentBalance - params.lastQueuedWithdrawAmount;
                                    {
                                        (performanceFeeInAsset, , totalVaultFee) = VaultLifecycleEarn
                                            .getVaultFees(
                                            balanceForVaultFees,
                                            vaultState.lastLockedAmount,
                                            vaultState.totalPending,
                                            params.performanceFee,
                                            params.managementFee
                                        );
                                    }
                                    // Take into account the fee
                                    // so we can calculate the newPricePerShare
                                    currentBalance = currentBalance - totalVaultFee;
                                    {
                                        newPricePerShare = ShareMath.pricePerShare(
                                            params.currentShareSupply - lastQueuedWithdrawShares,
                                            currentBalance - params.lastQueuedWithdrawAmount,
                                            pendingAmount,
                                            params.decimals
                                        );
                                        queuedWithdrawAmount =
                                            params.lastQueuedWithdrawAmount +
                                            ShareMath.sharesToAsset(
                                                params.currentQueuedWithdrawShares,
                                                newPricePerShare,
                                                params.decimals
                                            );
                                        // After closing the short, if the options expire in-the-money
                                        // vault pricePerShare would go down because vault's asset balance decreased.
                                        // This ensures that the newly-minted shares do not take on the loss.
                                        mintShares = ShareMath.assetToShares(
                                            pendingAmount,
                                            newPricePerShare,
                                            params.decimals
                                        );
                                    }
                                    return (
                                        currentBalance - queuedWithdrawAmount, // new locked balance subtracts the queued withdrawals
                                        queuedWithdrawAmount,
                                        newPricePerShare,
                                        mintShares,
                                        performanceFeeInAsset,
                                        totalVaultFee
                                    );
                                }
                                /**
                                 * @notice Calculates the performance and management fee for this week's round
                                 * @param currentBalance is the balance of funds held on the vault after closing short
                                 * @param lastLockedAmount is the amount of funds locked from the previous round
                                 * @param pendingAmount is the pending deposit amount
                                 * @param performanceFeePercent is the performance fee pct.
                                 * @param managementFeePercent is the management fee pct.
                                 * @return performanceFeeInAsset is the performance fee
                                 * @return managementFeeInAsset is the management fee
                                 * @return vaultFee is the total fees
                                 */
                                function getVaultFees(
                                    uint256 currentBalance,
                                    uint256 lastLockedAmount,
                                    uint256 pendingAmount,
                                    uint256 performanceFeePercent,
                                    uint256 managementFeePercent
                                )
                                    internal
                                    pure
                                    returns (
                                        uint256 performanceFeeInAsset,
                                        uint256 managementFeeInAsset,
                                        uint256 vaultFee
                                    )
                                {
                                    // At the first round, currentBalance=0, pendingAmount>0
                                    // so we just do not charge anything on the first round
                                    uint256 lockedBalanceSansPending =
                                        currentBalance > pendingAmount ? currentBalance - pendingAmount : 0;
                                    uint256 _performanceFeeInAsset;
                                    uint256 _managementFeeInAsset;
                                    uint256 _vaultFee;
                                    // Take performance fee and management fee ONLY if difference between
                                    // last week and this week's vault deposits, taking into account pending
                                    // deposits and withdrawals, is positive. If it is negative, last week's
                                    // option expired ITM past breakeven, and the vault took a loss so we
                                    // do not collect performance fee for last week
                                    if (lockedBalanceSansPending > lastLockedAmount) {
                                        _performanceFeeInAsset = performanceFeePercent > 0
                                            ? ((lockedBalanceSansPending - lastLockedAmount) *
                                                performanceFeePercent) / (100 * Vault.FEE_MULTIPLIER)
                                            : 0;
                                        _managementFeeInAsset = managementFeePercent > 0
                                            ? (lockedBalanceSansPending * managementFeePercent) /
                                                (100 * Vault.FEE_MULTIPLIER)
                                            : 0;
                                        _vaultFee = _performanceFeeInAsset + _managementFeeInAsset;
                                    }
                                    return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee);
                                }
                                /**
                                 * @notice Verify the constructor params satisfy requirements
                                 * @param feeRecipient is the address to recieve vault performance and management fees
                                 * @param optionSeller is the address of the entity that we will be buying options from (EX: Orbit)
                                 * @param performanceFee is the perfomance fee pct.
                                 * @param tokenName is the name of the token
                                 * @param tokenSymbol is the symbol of the token
                                 * @param _vaultParams is the struct with vault general data
                                 * @param _allocationState is the struct with vault loan/option allocation data
                                 */
                                function verifyInitializerParams(
                                    address keeper,
                                    address feeRecipient,
                                    address optionSeller,
                                    uint256 performanceFee,
                                    uint256 managementFee,
                                    string calldata tokenName,
                                    string calldata tokenSymbol,
                                    Vault.VaultParams calldata _vaultParams,
                                    Vault.AllocationState calldata _allocationState,
                                    uint256 totalPCT
                                ) external pure {
                                    require(keeper != address(0), "R7");
                                    require(feeRecipient != address(0), "R8");
                                    require(optionSeller != address(0), "R9");
                                    require(performanceFee < 100 * Vault.FEE_MULTIPLIER, "R12");
                                    require(managementFee < 100 * Vault.FEE_MULTIPLIER, "R11");
                                    require(bytes(tokenName).length > 0, "R41");
                                    require(bytes(tokenSymbol).length > 0, "R42");
                                    require(_vaultParams.asset != address(0), "R43");
                                    require(_vaultParams.minimumSupply > 0, "R44");
                                    require(_vaultParams.cap > 0, "R13");
                                    require(_vaultParams.cap > _vaultParams.minimumSupply, "R45");
                                    require(_allocationState.nextLoanTermLength == 0, "R46");
                                    require(_allocationState.nextOptionPurchaseFreq == 0, "R47");
                                    require(_allocationState.currentLoanTermLength >= 1 days, "R48");
                                    require(
                                        _allocationState.currentOptionPurchaseFreq > 0 &&
                                            _allocationState.currentOptionPurchaseFreq <=
                                            _allocationState.currentLoanTermLength,
                                        "R49"
                                    );
                                    require(
                                        uint256(_allocationState.loanAllocationPCT) +
                                            _allocationState.optionAllocationPCT <=
                                            totalPCT,
                                        "R50"
                                    );
                                    require(_allocationState.loanAllocation == 0, "R1");
                                    require(_allocationState.optionAllocation == 0, "R2");
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            import {Vault} from "./Vault.sol";
                            library ShareMath {
                                uint256 internal constant PLACEHOLDER_UINT = 1;
                                function assetToShares(
                                    uint256 assetAmount,
                                    uint256 assetPerShare,
                                    uint256 decimals
                                ) internal pure returns (uint256) {
                                    // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet
                                    // which should never happen.
                                    // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.
                                    require(assetPerShare > PLACEHOLDER_UINT, "Invalid assetPerShare");
                                    return (assetAmount * (10**decimals)) / assetPerShare;
                                }
                                function sharesToAsset(
                                    uint256 shares,
                                    uint256 assetPerShare,
                                    uint256 decimals
                                ) internal pure returns (uint256) {
                                    // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet
                                    // which should never happen.
                                    // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.
                                    require(assetPerShare > PLACEHOLDER_UINT, "Invalid assetPerShare");
                                    return (shares * assetPerShare) / (10**decimals);
                                }
                                /**
                                 * @notice Returns the shares unredeemed by the user given their DepositReceipt
                                 * @param depositReceipt is the user's deposit receipt
                                 * @param currentRound is the `round` stored on the vault
                                 * @param assetPerShare is the price in asset per share
                                 * @param decimals is the number of decimals the asset/shares use
                                 * @return unredeemedShares is the user's virtual balance of shares that are owed
                                 */
                                function getSharesFromReceipt(
                                    Vault.DepositReceipt memory depositReceipt,
                                    uint256 currentRound,
                                    uint256 assetPerShare,
                                    uint256 decimals
                                ) internal pure returns (uint256 unredeemedShares) {
                                    if (depositReceipt.round > 0 && depositReceipt.round < currentRound) {
                                        uint256 sharesFromRound =
                                            assetToShares(depositReceipt.amount, assetPerShare, decimals);
                                        return uint256(depositReceipt.unredeemedShares) + sharesFromRound;
                                    }
                                    return depositReceipt.unredeemedShares;
                                }
                                function pricePerShare(
                                    uint256 totalSupply,
                                    uint256 totalBalance,
                                    uint256 pendingAmount,
                                    uint256 decimals
                                ) internal pure returns (uint256) {
                                    uint256 singleShare = 10**decimals;
                                    return
                                        totalSupply > 0
                                            ? (singleShare * (totalBalance - pendingAmount)) / totalSupply
                                            : singleShare;
                                }
                                /************************************************
                                 *  HELPERS
                                 ***********************************************/
                                function assertUint104(uint256 num) internal pure {
                                    require(num <= type(uint104).max, "Overflow uint104");
                                }
                                function assertUint128(uint256 num) internal pure {
                                    require(num <= type(uint128).max, "Overflow uint128");
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            interface ILiquidityGauge {
                                function balanceOf(address) external view returns (uint256);
                                function deposit(
                                    uint256 _value,
                                    address _addr,
                                    bool _claim_rewards
                                ) external;
                                function withdraw(uint256 _value) external;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            interface IVaultPauser {
                                /// @notice pause vault position of an account with max amount
                                /// @param _account the address of user
                                /// @param _amount amount of shares
                                function pausePosition(address _account, uint256 _amount) external;
                                /// @notice resume vault position of an account with max amount
                                /// @param _vaultAddress the address of vault
                                function resumePosition(address _vaultAddress) external;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            interface IRibbonLend {
                                function balanceOf(address account) external view returns (uint256);
                                function getCurrentExchangeRate() external view returns (uint256);
                                function redeem(uint256 tokens) external;
                                function redeemCurrency(uint256 currencyAmount) external;
                                function provide(uint256 currencyAmount, address referral) external;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            /**
                             * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                             * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
                             * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                             * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                             *
                             * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                             * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                             *
                             * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                             * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                             */
                            abstract contract Initializable {
                                /**
                                 * @dev Indicates that the contract has been initialized.
                                 */
                                bool private _initialized;
                                /**
                                 * @dev Indicates that the contract is in the process of being initialized.
                                 */
                                bool private _initializing;
                                /**
                                 * @dev Modifier to protect an initializer function from being invoked twice.
                                 */
                                modifier initializer() {
                                    require(_initializing || !_initialized, "Initializable: contract is already initialized");
                                    bool isTopLevelCall = !_initializing;
                                    if (isTopLevelCall) {
                                        _initializing = true;
                                        _initialized = true;
                                    }
                                    _;
                                    if (isTopLevelCall) {
                                        _initializing = false;
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            import "../proxy/utils/Initializable.sol";
                            /**
                             * @dev Provides information about the current execution context, including the
                             * sender of the transaction and its data. While these are generally available
                             * via msg.sender and msg.data, they should not be accessed in such a direct
                             * manner, since when dealing with meta-transactions the account sending and
                             * paying for execution may not be the actual sender (as far as an application
                             * is concerned).
                             *
                             * This contract is only required for intermediate, library-like contracts.
                             */
                            abstract contract ContextUpgradeable is Initializable {
                                function __Context_init() internal initializer {
                                    __Context_init_unchained();
                                }
                                function __Context_init_unchained() internal initializer {
                                }
                                function _msgSender() internal view virtual returns (address) {
                                    return msg.sender;
                                }
                                function _msgData() internal view virtual returns (bytes calldata) {
                                    return msg.data;
                                }
                                uint256[50] private __gap;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Interface of the ERC20 standard as defined in the EIP.
                             */
                            interface IERC20Upgradeable {
                                /**
                                 * @dev Returns the amount of tokens in existence.
                                 */
                                function totalSupply() external view returns (uint256);
                                /**
                                 * @dev Returns the amount of tokens owned by `account`.
                                 */
                                function balanceOf(address account) external view returns (uint256);
                                /**
                                 * @dev Moves `amount` tokens from the caller's account to `recipient`.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * Emits a {Transfer} event.
                                 */
                                function transfer(address recipient, uint256 amount) external returns (bool);
                                /**
                                 * @dev Returns the remaining number of tokens that `spender` will be
                                 * allowed to spend on behalf of `owner` through {transferFrom}. This is
                                 * zero by default.
                                 *
                                 * This value changes when {approve} or {transferFrom} are called.
                                 */
                                function allowance(address owner, address spender) external view returns (uint256);
                                /**
                                 * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * IMPORTANT: Beware that changing an allowance with this method brings the risk
                                 * that someone may use both the old and the new allowance by unfortunate
                                 * transaction ordering. One possible solution to mitigate this race
                                 * condition is to first reduce the spender's allowance to 0 and set the
                                 * desired value afterwards:
                                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                                 *
                                 * Emits an {Approval} event.
                                 */
                                function approve(address spender, uint256 amount) external returns (bool);
                                /**
                                 * @dev Moves `amount` tokens from `sender` to `recipient` using the
                                 * allowance mechanism. `amount` is then deducted from the caller's
                                 * allowance.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * Emits a {Transfer} event.
                                 */
                                function transferFrom(
                                    address sender,
                                    address recipient,
                                    uint256 amount
                                ) external returns (bool);
                                /**
                                 * @dev Emitted when `value` tokens are moved from one account (`from`) to
                                 * another (`to`).
                                 *
                                 * Note that `value` may be zero.
                                 */
                                event Transfer(address indexed from, address indexed to, uint256 value);
                                /**
                                 * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                                 * a call to {approve}. `value` is the new allowance.
                                 */
                                event Approval(address indexed owner, address indexed spender, uint256 value);
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            import "../IERC20Upgradeable.sol";
                            /**
                             * @dev Interface for the optional metadata functions from the ERC20 standard.
                             *
                             * _Available since v4.1._
                             */
                            interface IERC20MetadataUpgradeable is IERC20Upgradeable {
                                /**
                                 * @dev Returns the name of the token.
                                 */
                                function name() external view returns (string memory);
                                /**
                                 * @dev Returns the symbol of the token.
                                 */
                                function symbol() external view returns (string memory);
                                /**
                                 * @dev Returns the decimals places of the token.
                                 */
                                function decimals() external view returns (uint8);
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Collection of functions related to the address type
                             */
                            library Address {
                                /**
                                 * @dev Returns true if `account` is a contract.
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * It is unsafe to assume that an address for which this function returns
                                 * false is an externally-owned account (EOA) and not a contract.
                                 *
                                 * Among others, `isContract` will return false for the following
                                 * types of addresses:
                                 *
                                 *  - an externally-owned account
                                 *  - a contract in construction
                                 *  - an address where a contract will be created
                                 *  - an address where a contract lived, but was destroyed
                                 * ====
                                 */
                                function isContract(address account) internal view returns (bool) {
                                    // This method relies on extcodesize, which returns 0 for contracts in
                                    // construction, since the code is only stored at the end of the
                                    // constructor execution.
                                    uint256 size;
                                    assembly {
                                        size := extcodesize(account)
                                    }
                                    return size > 0;
                                }
                                /**
                                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                                 * `recipient`, forwarding all available gas and reverting on errors.
                                 *
                                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                                 * imposed by `transfer`, making them unable to receive funds via
                                 * `transfer`. {sendValue} removes this limitation.
                                 *
                                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                                 *
                                 * IMPORTANT: because control is transferred to `recipient`, care must be
                                 * taken to not create reentrancy vulnerabilities. Consider using
                                 * {ReentrancyGuard} or the
                                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                                 */
                                function sendValue(address payable recipient, uint256 amount) internal {
                                    require(address(this).balance >= amount, "Address: insufficient balance");
                                    (bool success, ) = recipient.call{value: amount}("");
                                    require(success, "Address: unable to send value, recipient may have reverted");
                                }
                                /**
                                 * @dev Performs a Solidity function call using a low level `call`. A
                                 * plain `call` is an unsafe replacement for a function call: use this
                                 * function instead.
                                 *
                                 * If `target` reverts with a revert reason, it is bubbled up by this
                                 * function (like regular Solidity function calls).
                                 *
                                 * Returns the raw returned data. To convert to the expected return value,
                                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                                 *
                                 * Requirements:
                                 *
                                 * - `target` must be a contract.
                                 * - calling `target` with `data` must not revert.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionCall(target, data, "Address: low-level call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                                 * `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, 0, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but also transferring `value` wei to `target`.
                                 *
                                 * Requirements:
                                 *
                                 * - the calling contract must have an ETH balance of at least `value`.
                                 * - the called Solidity function must be `payable`.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value
                                ) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    require(address(this).balance >= value, "Address: insufficient balance for call");
                                    require(isContract(target), "Address: call to non-contract");
                                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                    return functionStaticCall(target, data, "Address: low-level static call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal view returns (bytes memory) {
                                    require(isContract(target), "Address: static call to non-contract");
                                    (bool success, bytes memory returndata) = target.staticcall(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a delegate call.
                                 *
                                 * _Available since v3.4._
                                 */
                                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a delegate call.
                                 *
                                 * _Available since v3.4._
                                 */
                                function functionDelegateCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    require(isContract(target), "Address: delegate call to non-contract");
                                    (bool success, bytes memory returndata) = target.delegatecall(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                                 * revert reason using the provided one.
                                 *
                                 * _Available since v4.3._
                                 */
                                function verifyCallResult(
                                    bool success,
                                    bytes memory returndata,
                                    string memory errorMessage
                                ) internal pure returns (bytes memory) {
                                    if (success) {
                                        return returndata;
                                    } else {
                                        // Look for revert reason and bubble it up if present
                                        if (returndata.length > 0) {
                                            // The easiest way to bubble the revert reason is using memory via assembly
                                            assembly {
                                                let returndata_size := mload(returndata)
                                                revert(add(32, returndata), returndata_size)
                                            }
                                        } else {
                                            revert(errorMessage);
                                        }
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity =0.8.4;
                            import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                            import {
                                SafeERC20
                            } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                            /**
                             * This library supports ERC20s that have quirks in their behavior.
                             * One such ERC20 is USDT, which requires allowance to be 0 before calling approve.
                             * We plan to update this library with ERC20s that display such idiosyncratic behavior.
                             */
                            library SupportsNonCompliantERC20 {
                                address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
                                function safeApproveNonCompliant(
                                    IERC20 token,
                                    address spender,
                                    uint256 amount
                                ) internal {
                                    if (address(token) == USDT) {
                                        SafeERC20.safeApprove(token, spender, 0);
                                    }
                                    SafeERC20.safeApprove(token, spender, amount);
                                }
                            }
                            

                            File 4 of 8: FiatTokenV2_1
                            // File: @openzeppelin/contracts/math/SafeMath.sol
                            
                            // SPDX-License-Identifier: MIT
                            
                            pragma solidity ^0.6.0;
                            
                            /**
                             * @dev Wrappers over Solidity's arithmetic operations with added overflow
                             * checks.
                             *
                             * Arithmetic operations in Solidity wrap on overflow. This can easily result
                             * in bugs, because programmers usually assume that an overflow raises an
                             * error, which is the standard behavior in high level programming languages.
                             * `SafeMath` restores this intuition by reverting the transaction when an
                             * operation overflows.
                             *
                             * Using this library instead of the unchecked operations eliminates an entire
                             * class of bugs, so it's recommended to use it always.
                             */
                            library SafeMath {
                                /**
                                 * @dev Returns the addition of two unsigned integers, reverting on
                                 * overflow.
                                 *
                                 * Counterpart to Solidity's `+` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - Addition cannot overflow.
                                 */
                                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                    uint256 c = a + b;
                                    require(c >= a, "SafeMath: addition overflow");
                            
                                    return c;
                                }
                            
                                /**
                                 * @dev Returns the subtraction of two unsigned integers, reverting on
                                 * overflow (when the result is negative).
                                 *
                                 * Counterpart to Solidity's `-` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - Subtraction cannot overflow.
                                 */
                                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                    return sub(a, b, "SafeMath: subtraction overflow");
                                }
                            
                                /**
                                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                                 * overflow (when the result is negative).
                                 *
                                 * Counterpart to Solidity's `-` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - Subtraction cannot overflow.
                                 */
                                function sub(
                                    uint256 a,
                                    uint256 b,
                                    string memory errorMessage
                                ) internal pure returns (uint256) {
                                    require(b <= a, errorMessage);
                                    uint256 c = a - b;
                            
                                    return c;
                                }
                            
                                /**
                                 * @dev Returns the multiplication of two unsigned integers, reverting on
                                 * overflow.
                                 *
                                 * Counterpart to Solidity's `*` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - Multiplication cannot overflow.
                                 */
                                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                                    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                                    // benefit is lost if 'b' is also tested.
                                    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                                    if (a == 0) {
                                        return 0;
                                    }
                            
                                    uint256 c = a * b;
                                    require(c / a == b, "SafeMath: multiplication overflow");
                            
                                    return c;
                                }
                            
                                /**
                                 * @dev Returns the integer division of two unsigned integers. Reverts on
                                 * division by zero. The result is rounded towards zero.
                                 *
                                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                                 * uses an invalid opcode to revert (consuming all remaining gas).
                                 *
                                 * Requirements:
                                 *
                                 * - The divisor cannot be zero.
                                 */
                                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                    return div(a, b, "SafeMath: division by zero");
                                }
                            
                                /**
                                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                                 * division by zero. The result is rounded towards zero.
                                 *
                                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                                 * uses an invalid opcode to revert (consuming all remaining gas).
                                 *
                                 * Requirements:
                                 *
                                 * - The divisor cannot be zero.
                                 */
                                function div(
                                    uint256 a,
                                    uint256 b,
                                    string memory errorMessage
                                ) internal pure returns (uint256) {
                                    require(b > 0, errorMessage);
                                    uint256 c = a / b;
                                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                            
                                    return c;
                                }
                            
                                /**
                                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                                 * Reverts when dividing by zero.
                                 *
                                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                                 * invalid opcode to revert (consuming all remaining gas).
                                 *
                                 * Requirements:
                                 *
                                 * - The divisor cannot be zero.
                                 */
                                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                    return mod(a, b, "SafeMath: modulo by zero");
                                }
                            
                                /**
                                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                                 * Reverts with custom message when dividing by zero.
                                 *
                                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                                 * invalid opcode to revert (consuming all remaining gas).
                                 *
                                 * Requirements:
                                 *
                                 * - The divisor cannot be zero.
                                 */
                                function mod(
                                    uint256 a,
                                    uint256 b,
                                    string memory errorMessage
                                ) internal pure returns (uint256) {
                                    require(b != 0, errorMessage);
                                    return a % b;
                                }
                            }
                            
                            // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
                            
                            pragma solidity ^0.6.0;
                            
                            /**
                             * @dev Interface of the ERC20 standard as defined in the EIP.
                             */
                            interface IERC20 {
                                /**
                                 * @dev Returns the amount of tokens in existence.
                                 */
                                function totalSupply() external view returns (uint256);
                            
                                /**
                                 * @dev Returns the amount of tokens owned by `account`.
                                 */
                                function balanceOf(address account) external view returns (uint256);
                            
                                /**
                                 * @dev Moves `amount` tokens from the caller's account to `recipient`.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * Emits a {Transfer} event.
                                 */
                                function transfer(address recipient, uint256 amount)
                                    external
                                    returns (bool);
                            
                                /**
                                 * @dev Returns the remaining number of tokens that `spender` will be
                                 * allowed to spend on behalf of `owner` through {transferFrom}. This is
                                 * zero by default.
                                 *
                                 * This value changes when {approve} or {transferFrom} are called.
                                 */
                                function allowance(address owner, address spender)
                                    external
                                    view
                                    returns (uint256);
                            
                                /**
                                 * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * IMPORTANT: Beware that changing an allowance with this method brings the risk
                                 * that someone may use both the old and the new allowance by unfortunate
                                 * transaction ordering. One possible solution to mitigate this race
                                 * condition is to first reduce the spender's allowance to 0 and set the
                                 * desired value afterwards:
                                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                                 *
                                 * Emits an {Approval} event.
                                 */
                                function approve(address spender, uint256 amount) external returns (bool);
                            
                                /**
                                 * @dev Moves `amount` tokens from `sender` to `recipient` using the
                                 * allowance mechanism. `amount` is then deducted from the caller's
                                 * allowance.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * Emits a {Transfer} event.
                                 */
                                function transferFrom(
                                    address sender,
                                    address recipient,
                                    uint256 amount
                                ) external returns (bool);
                            
                                /**
                                 * @dev Emitted when `value` tokens are moved from one account (`from`) to
                                 * another (`to`).
                                 *
                                 * Note that `value` may be zero.
                                 */
                                event Transfer(address indexed from, address indexed to, uint256 value);
                            
                                /**
                                 * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                                 * a call to {approve}. `value` is the new allowance.
                                 */
                                event Approval(
                                    address indexed owner,
                                    address indexed spender,
                                    uint256 value
                                );
                            }
                            
                            // File: contracts/v1/AbstractFiatTokenV1.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            abstract contract AbstractFiatTokenV1 is IERC20 {
                                function _approve(
                                    address owner,
                                    address spender,
                                    uint256 value
                                ) internal virtual;
                            
                                function _transfer(
                                    address from,
                                    address to,
                                    uint256 value
                                ) internal virtual;
                            }
                            
                            // File: contracts/v1/Ownable.sol
                            
                            /**
                             * Copyright (c) 2018 zOS Global Limited.
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            pragma solidity 0.6.12;
                            
                            /**
                             * @notice The Ownable contract has an owner address, and provides basic
                             * authorization control functions
                             * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol
                             * Modifications:
                             * 1. Consolidate OwnableStorage into this contract (7/13/18)
                             * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
                             * 3. Make public functions external (5/27/20)
                             */
                            contract Ownable {
                                // Owner of the contract
                                address private _owner;
                            
                                /**
                                 * @dev Event to show ownership has been transferred
                                 * @param previousOwner representing the address of the previous owner
                                 * @param newOwner representing the address of the new owner
                                 */
                                event OwnershipTransferred(address previousOwner, address newOwner);
                            
                                /**
                                 * @dev The constructor sets the original owner of the contract to the sender account.
                                 */
                                constructor() public {
                                    setOwner(msg.sender);
                                }
                            
                                /**
                                 * @dev Tells the address of the owner
                                 * @return the address of the owner
                                 */
                                function owner() external view returns (address) {
                                    return _owner;
                                }
                            
                                /**
                                 * @dev Sets a new owner address
                                 */
                                function setOwner(address newOwner) internal {
                                    _owner = newOwner;
                                }
                            
                                /**
                                 * @dev Throws if called by any account other than the owner.
                                 */
                                modifier onlyOwner() {
                                    require(msg.sender == _owner, "Ownable: caller is not the owner");
                                    _;
                                }
                            
                                /**
                                 * @dev Allows the current owner to transfer control of the contract to a newOwner.
                                 * @param newOwner The address to transfer ownership to.
                                 */
                                function transferOwnership(address newOwner) external onlyOwner {
                                    require(
                                        newOwner != address(0),
                                        "Ownable: new owner is the zero address"
                                    );
                                    emit OwnershipTransferred(_owner, newOwner);
                                    setOwner(newOwner);
                                }
                            }
                            
                            // File: contracts/v1/Pausable.sol
                            
                            /**
                             * Copyright (c) 2016 Smart Contract Solutions, Inc.
                             * Copyright (c) 2018-2020 CENTRE SECZ0
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @notice Base contract which allows children to implement an emergency stop
                             * mechanism
                             * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol
                             * Modifications:
                             * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
                             * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
                             * 3. Removed whenPaused (6/14/2018)
                             * 4. Switches ownable library to use ZeppelinOS (7/12/18)
                             * 5. Remove constructor (7/13/18)
                             * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20)
                             * 7. Make public functions external (5/27/20)
                             */
                            contract Pausable is Ownable {
                                event Pause();
                                event Unpause();
                                event PauserChanged(address indexed newAddress);
                            
                                address public pauser;
                                bool public paused = false;
                            
                                /**
                                 * @dev Modifier to make a function callable only when the contract is not paused.
                                 */
                                modifier whenNotPaused() {
                                    require(!paused, "Pausable: paused");
                                    _;
                                }
                            
                                /**
                                 * @dev throws if called by any account other than the pauser
                                 */
                                modifier onlyPauser() {
                                    require(msg.sender == pauser, "Pausable: caller is not the pauser");
                                    _;
                                }
                            
                                /**
                                 * @dev called by the owner to pause, triggers stopped state
                                 */
                                function pause() external onlyPauser {
                                    paused = true;
                                    emit Pause();
                                }
                            
                                /**
                                 * @dev called by the owner to unpause, returns to normal state
                                 */
                                function unpause() external onlyPauser {
                                    paused = false;
                                    emit Unpause();
                                }
                            
                                /**
                                 * @dev update the pauser role
                                 */
                                function updatePauser(address _newPauser) external onlyOwner {
                                    require(
                                        _newPauser != address(0),
                                        "Pausable: new pauser is the zero address"
                                    );
                                    pauser = _newPauser;
                                    emit PauserChanged(pauser);
                                }
                            }
                            
                            // File: contracts/v1/Blacklistable.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title Blacklistable Token
                             * @dev Allows accounts to be blacklisted by a "blacklister" role
                             */
                            contract Blacklistable is Ownable {
                                address public blacklister;
                                mapping(address => bool) internal blacklisted;
                            
                                event Blacklisted(address indexed _account);
                                event UnBlacklisted(address indexed _account);
                                event BlacklisterChanged(address indexed newBlacklister);
                            
                                /**
                                 * @dev Throws if called by any account other than the blacklister
                                 */
                                modifier onlyBlacklister() {
                                    require(
                                        msg.sender == blacklister,
                                        "Blacklistable: caller is not the blacklister"
                                    );
                                    _;
                                }
                            
                                /**
                                 * @dev Throws if argument account is blacklisted
                                 * @param _account The address to check
                                 */
                                modifier notBlacklisted(address _account) {
                                    require(
                                        !blacklisted[_account],
                                        "Blacklistable: account is blacklisted"
                                    );
                                    _;
                                }
                            
                                /**
                                 * @dev Checks if account is blacklisted
                                 * @param _account The address to check
                                 */
                                function isBlacklisted(address _account) external view returns (bool) {
                                    return blacklisted[_account];
                                }
                            
                                /**
                                 * @dev Adds account to blacklist
                                 * @param _account The address to blacklist
                                 */
                                function blacklist(address _account) external onlyBlacklister {
                                    blacklisted[_account] = true;
                                    emit Blacklisted(_account);
                                }
                            
                                /**
                                 * @dev Removes account from blacklist
                                 * @param _account The address to remove from the blacklist
                                 */
                                function unBlacklist(address _account) external onlyBlacklister {
                                    blacklisted[_account] = false;
                                    emit UnBlacklisted(_account);
                                }
                            
                                function updateBlacklister(address _newBlacklister) external onlyOwner {
                                    require(
                                        _newBlacklister != address(0),
                                        "Blacklistable: new blacklister is the zero address"
                                    );
                                    blacklister = _newBlacklister;
                                    emit BlacklisterChanged(blacklister);
                                }
                            }
                            
                            // File: contracts/v1/FiatTokenV1.sol
                            
                            /**
                             *
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title FiatToken
                             * @dev ERC20 Token backed by fiat reserves
                             */
                            contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable {
                                using SafeMath for uint256;
                            
                                string public name;
                                string public symbol;
                                uint8 public decimals;
                                string public currency;
                                address public masterMinter;
                                bool internal initialized;
                            
                                mapping(address => uint256) internal balances;
                                mapping(address => mapping(address => uint256)) internal allowed;
                                uint256 internal totalSupply_ = 0;
                                mapping(address => bool) internal minters;
                                mapping(address => uint256) internal minterAllowed;
                            
                                event Mint(address indexed minter, address indexed to, uint256 amount);
                                event Burn(address indexed burner, uint256 amount);
                                event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
                                event MinterRemoved(address indexed oldMinter);
                                event MasterMinterChanged(address indexed newMasterMinter);
                            
                                function initialize(
                                    string memory tokenName,
                                    string memory tokenSymbol,
                                    string memory tokenCurrency,
                                    uint8 tokenDecimals,
                                    address newMasterMinter,
                                    address newPauser,
                                    address newBlacklister,
                                    address newOwner
                                ) public {
                                    require(!initialized, "FiatToken: contract is already initialized");
                                    require(
                                        newMasterMinter != address(0),
                                        "FiatToken: new masterMinter is the zero address"
                                    );
                                    require(
                                        newPauser != address(0),
                                        "FiatToken: new pauser is the zero address"
                                    );
                                    require(
                                        newBlacklister != address(0),
                                        "FiatToken: new blacklister is the zero address"
                                    );
                                    require(
                                        newOwner != address(0),
                                        "FiatToken: new owner is the zero address"
                                    );
                            
                                    name = tokenName;
                                    symbol = tokenSymbol;
                                    currency = tokenCurrency;
                                    decimals = tokenDecimals;
                                    masterMinter = newMasterMinter;
                                    pauser = newPauser;
                                    blacklister = newBlacklister;
                                    setOwner(newOwner);
                                    initialized = true;
                                }
                            
                                /**
                                 * @dev Throws if called by any account other than a minter
                                 */
                                modifier onlyMinters() {
                                    require(minters[msg.sender], "FiatToken: caller is not a minter");
                                    _;
                                }
                            
                                /**
                                 * @dev Function to mint tokens
                                 * @param _to The address that will receive the minted tokens.
                                 * @param _amount The amount of tokens to mint. Must be less than or equal
                                 * to the minterAllowance of the caller.
                                 * @return A boolean that indicates if the operation was successful.
                                 */
                                function mint(address _to, uint256 _amount)
                                    external
                                    whenNotPaused
                                    onlyMinters
                                    notBlacklisted(msg.sender)
                                    notBlacklisted(_to)
                                    returns (bool)
                                {
                                    require(_to != address(0), "FiatToken: mint to the zero address");
                                    require(_amount > 0, "FiatToken: mint amount not greater than 0");
                            
                                    uint256 mintingAllowedAmount = minterAllowed[msg.sender];
                                    require(
                                        _amount <= mintingAllowedAmount,
                                        "FiatToken: mint amount exceeds minterAllowance"
                                    );
                            
                                    totalSupply_ = totalSupply_.add(_amount);
                                    balances[_to] = balances[_to].add(_amount);
                                    minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
                                    emit Mint(msg.sender, _to, _amount);
                                    emit Transfer(address(0), _to, _amount);
                                    return true;
                                }
                            
                                /**
                                 * @dev Throws if called by any account other than the masterMinter
                                 */
                                modifier onlyMasterMinter() {
                                    require(
                                        msg.sender == masterMinter,
                                        "FiatToken: caller is not the masterMinter"
                                    );
                                    _;
                                }
                            
                                /**
                                 * @dev Get minter allowance for an account
                                 * @param minter The address of the minter
                                 */
                                function minterAllowance(address minter) external view returns (uint256) {
                                    return minterAllowed[minter];
                                }
                            
                                /**
                                 * @dev Checks if account is a minter
                                 * @param account The address to check
                                 */
                                function isMinter(address account) external view returns (bool) {
                                    return minters[account];
                                }
                            
                                /**
                                 * @notice Amount of remaining tokens spender is allowed to transfer on
                                 * behalf of the token owner
                                 * @param owner     Token owner's address
                                 * @param spender   Spender's address
                                 * @return Allowance amount
                                 */
                                function allowance(address owner, address spender)
                                    external
                                    override
                                    view
                                    returns (uint256)
                                {
                                    return allowed[owner][spender];
                                }
                            
                                /**
                                 * @dev Get totalSupply of token
                                 */
                                function totalSupply() external override view returns (uint256) {
                                    return totalSupply_;
                                }
                            
                                /**
                                 * @dev Get token balance of an account
                                 * @param account address The account
                                 */
                                function balanceOf(address account)
                                    external
                                    override
                                    view
                                    returns (uint256)
                                {
                                    return balances[account];
                                }
                            
                                /**
                                 * @notice Set spender's allowance over the caller's tokens to be a given
                                 * value.
                                 * @param spender   Spender's address
                                 * @param value     Allowance amount
                                 * @return True if successful
                                 */
                                function approve(address spender, uint256 value)
                                    external
                                    override
                                    whenNotPaused
                                    notBlacklisted(msg.sender)
                                    notBlacklisted(spender)
                                    returns (bool)
                                {
                                    _approve(msg.sender, spender, value);
                                    return true;
                                }
                            
                                /**
                                 * @dev Internal function to set allowance
                                 * @param owner     Token owner's address
                                 * @param spender   Spender's address
                                 * @param value     Allowance amount
                                 */
                                function _approve(
                                    address owner,
                                    address spender,
                                    uint256 value
                                ) internal override {
                                    require(owner != address(0), "ERC20: approve from the zero address");
                                    require(spender != address(0), "ERC20: approve to the zero address");
                                    allowed[owner][spender] = value;
                                    emit Approval(owner, spender, value);
                                }
                            
                                /**
                                 * @notice Transfer tokens by spending allowance
                                 * @param from  Payer's address
                                 * @param to    Payee's address
                                 * @param value Transfer amount
                                 * @return True if successful
                                 */
                                function transferFrom(
                                    address from,
                                    address to,
                                    uint256 value
                                )
                                    external
                                    override
                                    whenNotPaused
                                    notBlacklisted(msg.sender)
                                    notBlacklisted(from)
                                    notBlacklisted(to)
                                    returns (bool)
                                {
                                    require(
                                        value <= allowed[from][msg.sender],
                                        "ERC20: transfer amount exceeds allowance"
                                    );
                                    _transfer(from, to, value);
                                    allowed[from][msg.sender] = allowed[from][msg.sender].sub(value);
                                    return true;
                                }
                            
                                /**
                                 * @notice Transfer tokens from the caller
                                 * @param to    Payee's address
                                 * @param value Transfer amount
                                 * @return True if successful
                                 */
                                function transfer(address to, uint256 value)
                                    external
                                    override
                                    whenNotPaused
                                    notBlacklisted(msg.sender)
                                    notBlacklisted(to)
                                    returns (bool)
                                {
                                    _transfer(msg.sender, to, value);
                                    return true;
                                }
                            
                                /**
                                 * @notice Internal function to process transfers
                                 * @param from  Payer's address
                                 * @param to    Payee's address
                                 * @param value Transfer amount
                                 */
                                function _transfer(
                                    address from,
                                    address to,
                                    uint256 value
                                ) internal override {
                                    require(from != address(0), "ERC20: transfer from the zero address");
                                    require(to != address(0), "ERC20: transfer to the zero address");
                                    require(
                                        value <= balances[from],
                                        "ERC20: transfer amount exceeds balance"
                                    );
                            
                                    balances[from] = balances[from].sub(value);
                                    balances[to] = balances[to].add(value);
                                    emit Transfer(from, to, value);
                                }
                            
                                /**
                                 * @dev Function to add/update a new minter
                                 * @param minter The address of the minter
                                 * @param minterAllowedAmount The minting amount allowed for the minter
                                 * @return True if the operation was successful.
                                 */
                                function configureMinter(address minter, uint256 minterAllowedAmount)
                                    external
                                    whenNotPaused
                                    onlyMasterMinter
                                    returns (bool)
                                {
                                    minters[minter] = true;
                                    minterAllowed[minter] = minterAllowedAmount;
                                    emit MinterConfigured(minter, minterAllowedAmount);
                                    return true;
                                }
                            
                                /**
                                 * @dev Function to remove a minter
                                 * @param minter The address of the minter to remove
                                 * @return True if the operation was successful.
                                 */
                                function removeMinter(address minter)
                                    external
                                    onlyMasterMinter
                                    returns (bool)
                                {
                                    minters[minter] = false;
                                    minterAllowed[minter] = 0;
                                    emit MinterRemoved(minter);
                                    return true;
                                }
                            
                                /**
                                 * @dev allows a minter to burn some of its own tokens
                                 * Validates that caller is a minter and that sender is not blacklisted
                                 * amount is less than or equal to the minter's account balance
                                 * @param _amount uint256 the amount of tokens to be burned
                                 */
                                function burn(uint256 _amount)
                                    external
                                    whenNotPaused
                                    onlyMinters
                                    notBlacklisted(msg.sender)
                                {
                                    uint256 balance = balances[msg.sender];
                                    require(_amount > 0, "FiatToken: burn amount not greater than 0");
                                    require(balance >= _amount, "FiatToken: burn amount exceeds balance");
                            
                                    totalSupply_ = totalSupply_.sub(_amount);
                                    balances[msg.sender] = balance.sub(_amount);
                                    emit Burn(msg.sender, _amount);
                                    emit Transfer(msg.sender, address(0), _amount);
                                }
                            
                                function updateMasterMinter(address _newMasterMinter) external onlyOwner {
                                    require(
                                        _newMasterMinter != address(0),
                                        "FiatToken: new masterMinter is the zero address"
                                    );
                                    masterMinter = _newMasterMinter;
                                    emit MasterMinterChanged(masterMinter);
                                }
                            }
                            
                            // File: @openzeppelin/contracts/utils/Address.sol
                            
                            pragma solidity ^0.6.2;
                            
                            /**
                             * @dev Collection of functions related to the address type
                             */
                            library Address {
                                /**
                                 * @dev Returns true if `account` is a contract.
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * It is unsafe to assume that an address for which this function returns
                                 * false is an externally-owned account (EOA) and not a contract.
                                 *
                                 * Among others, `isContract` will return false for the following
                                 * types of addresses:
                                 *
                                 *  - an externally-owned account
                                 *  - a contract in construction
                                 *  - an address where a contract will be created
                                 *  - an address where a contract lived, but was destroyed
                                 * ====
                                 */
                                function isContract(address account) internal view returns (bool) {
                                    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                                    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                                    // for accounts without code, i.e. `keccak256('')`
                                    bytes32 codehash;
                            
                                        bytes32 accountHash
                                     = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                                    // solhint-disable-next-line no-inline-assembly
                                    assembly {
                                        codehash := extcodehash(account)
                                    }
                                    return (codehash != accountHash && codehash != 0x0);
                                }
                            
                                /**
                                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                                 * `recipient`, forwarding all available gas and reverting on errors.
                                 *
                                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                                 * imposed by `transfer`, making them unable to receive funds via
                                 * `transfer`. {sendValue} removes this limitation.
                                 *
                                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                                 *
                                 * IMPORTANT: because control is transferred to `recipient`, care must be
                                 * taken to not create reentrancy vulnerabilities. Consider using
                                 * {ReentrancyGuard} or the
                                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                                 */
                                function sendValue(address payable recipient, uint256 amount) internal {
                                    require(
                                        address(this).balance >= amount,
                                        "Address: insufficient balance"
                                    );
                            
                                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                    (bool success, ) = recipient.call{ value: amount }("");
                                    require(
                                        success,
                                        "Address: unable to send value, recipient may have reverted"
                                    );
                                }
                            
                                /**
                                 * @dev Performs a Solidity function call using a low level `call`. A
                                 * plain`call` is an unsafe replacement for a function call: use this
                                 * function instead.
                                 *
                                 * If `target` reverts with a revert reason, it is bubbled up by this
                                 * function (like regular Solidity function calls).
                                 *
                                 * Returns the raw returned data. To convert to the expected return value,
                                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                                 *
                                 * Requirements:
                                 *
                                 * - `target` must be a contract.
                                 * - calling `target` with `data` must not revert.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(address target, bytes memory data)
                                    internal
                                    returns (bytes memory)
                                {
                                    return functionCall(target, data, "Address: low-level call failed");
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                                 * `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    return _functionCallWithValue(target, data, 0, errorMessage);
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but also transferring `value` wei to `target`.
                                 *
                                 * Requirements:
                                 *
                                 * - the calling contract must have an ETH balance of at least `value`.
                                 * - the called Solidity function must be `payable`.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value
                                ) internal returns (bytes memory) {
                                    return
                                        functionCallWithValue(
                                            target,
                                            data,
                                            value,
                                            "Address: low-level call with value failed"
                                        );
                                }
                            
                                /**
                                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    require(
                                        address(this).balance >= value,
                                        "Address: insufficient balance for call"
                                    );
                                    return _functionCallWithValue(target, data, value, errorMessage);
                                }
                            
                                function _functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 weiValue,
                                    string memory errorMessage
                                ) private returns (bytes memory) {
                                    require(isContract(target), "Address: call to non-contract");
                            
                                    // solhint-disable-next-line avoid-low-level-calls
                                    (bool success, bytes memory returndata) = target.call{
                                        value: weiValue
                                    }(data);
                                    if (success) {
                                        return returndata;
                                    } else {
                                        // Look for revert reason and bubble it up if present
                                        if (returndata.length > 0) {
                                            // The easiest way to bubble the revert reason is using memory via assembly
                            
                                            // solhint-disable-next-line no-inline-assembly
                                            assembly {
                                                let returndata_size := mload(returndata)
                                                revert(add(32, returndata), returndata_size)
                                            }
                                        } else {
                                            revert(errorMessage);
                                        }
                                    }
                                }
                            }
                            
                            // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
                            
                            pragma solidity ^0.6.0;
                            
                            /**
                             * @title SafeERC20
                             * @dev Wrappers around ERC20 operations that throw on failure (when the token
                             * contract returns false). Tokens that return no value (and instead revert or
                             * throw on failure) are also supported, non-reverting calls are assumed to be
                             * successful.
                             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                             */
                            library SafeERC20 {
                                using SafeMath for uint256;
                                using Address for address;
                            
                                function safeTransfer(
                                    IERC20 token,
                                    address to,
                                    uint256 value
                                ) internal {
                                    _callOptionalReturn(
                                        token,
                                        abi.encodeWithSelector(token.transfer.selector, to, value)
                                    );
                                }
                            
                                function safeTransferFrom(
                                    IERC20 token,
                                    address from,
                                    address to,
                                    uint256 value
                                ) internal {
                                    _callOptionalReturn(
                                        token,
                                        abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
                                    );
                                }
                            
                                /**
                                 * @dev Deprecated. This function has issues similar to the ones found in
                                 * {IERC20-approve}, and its usage is discouraged.
                                 *
                                 * Whenever possible, use {safeIncreaseAllowance} and
                                 * {safeDecreaseAllowance} instead.
                                 */
                                function safeApprove(
                                    IERC20 token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    // safeApprove should only be called when setting an initial allowance,
                                    // or when resetting it to zero. To increase and decrease it, use
                                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                                    // solhint-disable-next-line max-line-length
                                    require(
                                        (value == 0) || (token.allowance(address(this), spender) == 0),
                                        "SafeERC20: approve from non-zero to non-zero allowance"
                                    );
                                    _callOptionalReturn(
                                        token,
                                        abi.encodeWithSelector(token.approve.selector, spender, value)
                                    );
                                }
                            
                                function safeIncreaseAllowance(
                                    IERC20 token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    uint256 newAllowance = token.allowance(address(this), spender).add(
                                        value
                                    );
                                    _callOptionalReturn(
                                        token,
                                        abi.encodeWithSelector(
                                            token.approve.selector,
                                            spender,
                                            newAllowance
                                        )
                                    );
                                }
                            
                                function safeDecreaseAllowance(
                                    IERC20 token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    uint256 newAllowance = token.allowance(address(this), spender).sub(
                                        value,
                                        "SafeERC20: decreased allowance below zero"
                                    );
                                    _callOptionalReturn(
                                        token,
                                        abi.encodeWithSelector(
                                            token.approve.selector,
                                            spender,
                                            newAllowance
                                        )
                                    );
                                }
                            
                                /**
                                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                                 * @param token The token targeted by the call.
                                 * @param data The call data (encoded using abi.encode or one of its variants).
                                 */
                                function _callOptionalReturn(IERC20 token, bytes memory data) private {
                                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                                    // the target address contains contract code and also asserts for success in the low-level call.
                            
                                    bytes memory returndata = address(token).functionCall(
                                        data,
                                        "SafeERC20: low-level call failed"
                                    );
                                    if (returndata.length > 0) {
                                        // Return data is optional
                                        // solhint-disable-next-line max-line-length
                                        require(
                                            abi.decode(returndata, (bool)),
                                            "SafeERC20: ERC20 operation did not succeed"
                                        );
                                    }
                                }
                            }
                            
                            // File: contracts/v1.1/Rescuable.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            contract Rescuable is Ownable {
                                using SafeERC20 for IERC20;
                            
                                address private _rescuer;
                            
                                event RescuerChanged(address indexed newRescuer);
                            
                                /**
                                 * @notice Returns current rescuer
                                 * @return Rescuer's address
                                 */
                                function rescuer() external view returns (address) {
                                    return _rescuer;
                                }
                            
                                /**
                                 * @notice Revert if called by any account other than the rescuer.
                                 */
                                modifier onlyRescuer() {
                                    require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer");
                                    _;
                                }
                            
                                /**
                                 * @notice Rescue ERC20 tokens locked up in this contract.
                                 * @param tokenContract ERC20 token contract address
                                 * @param to        Recipient address
                                 * @param amount    Amount to withdraw
                                 */
                                function rescueERC20(
                                    IERC20 tokenContract,
                                    address to,
                                    uint256 amount
                                ) external onlyRescuer {
                                    tokenContract.safeTransfer(to, amount);
                                }
                            
                                /**
                                 * @notice Assign the rescuer role to a given address.
                                 * @param newRescuer New rescuer's address
                                 */
                                function updateRescuer(address newRescuer) external onlyOwner {
                                    require(
                                        newRescuer != address(0),
                                        "Rescuable: new rescuer is the zero address"
                                    );
                                    _rescuer = newRescuer;
                                    emit RescuerChanged(newRescuer);
                                }
                            }
                            
                            // File: contracts/v1.1/FiatTokenV1_1.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title FiatTokenV1_1
                             * @dev ERC20 Token backed by fiat reserves
                             */
                            contract FiatTokenV1_1 is FiatTokenV1, Rescuable {
                            
                            }
                            
                            // File: contracts/v2/AbstractFiatTokenV2.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 {
                                function _increaseAllowance(
                                    address owner,
                                    address spender,
                                    uint256 increment
                                ) internal virtual;
                            
                                function _decreaseAllowance(
                                    address owner,
                                    address spender,
                                    uint256 decrement
                                ) internal virtual;
                            }
                            
                            // File: contracts/util/ECRecover.sol
                            
                            /**
                             * Copyright (c) 2016-2019 zOS Global Limited
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title ECRecover
                             * @notice A library that provides a safe ECDSA recovery function
                             */
                            library ECRecover {
                                /**
                                 * @notice Recover signer's address from a signed message
                                 * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol
                                 * Modifications: Accept v, r, and s as separate arguments
                                 * @param digest    Keccak-256 hash digest of the signed message
                                 * @param v         v of the signature
                                 * @param r         r of the signature
                                 * @param s         s of the signature
                                 * @return Signer address
                                 */
                                function recover(
                                    bytes32 digest,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) internal pure returns (address) {
                                    // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                                    // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                                    // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                                    // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                                    //
                                    // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                                    // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                                    // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                                    // these malleable signatures as well.
                                    if (
                                        uint256(s) >
                                        0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
                                    ) {
                                        revert("ECRecover: invalid signature 's' value");
                                    }
                            
                                    if (v != 27 && v != 28) {
                                        revert("ECRecover: invalid signature 'v' value");
                                    }
                            
                                    // If the signature is valid (and not malleable), return the signer address
                                    address signer = ecrecover(digest, v, r, s);
                                    require(signer != address(0), "ECRecover: invalid signature");
                            
                                    return signer;
                                }
                            }
                            
                            // File: contracts/util/EIP712.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title EIP712
                             * @notice A library that provides EIP712 helper functions
                             */
                            library EIP712 {
                                /**
                                 * @notice Make EIP712 domain separator
                                 * @param name      Contract name
                                 * @param version   Contract version
                                 * @return Domain separator
                                 */
                                function makeDomainSeparator(string memory name, string memory version)
                                    internal
                                    view
                                    returns (bytes32)
                                {
                                    uint256 chainId;
                                    assembly {
                                        chainId := chainid()
                                    }
                                    return
                                        keccak256(
                                            abi.encode(
                                                // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                                                0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                                                keccak256(bytes(name)),
                                                keccak256(bytes(version)),
                                                chainId,
                                                address(this)
                                            )
                                        );
                                }
                            
                                /**
                                 * @notice Recover signer's address from a EIP712 signature
                                 * @param domainSeparator   Domain separator
                                 * @param v                 v of the signature
                                 * @param r                 r of the signature
                                 * @param s                 s of the signature
                                 * @param typeHashAndData   Type hash concatenated with data
                                 * @return Signer's address
                                 */
                                function recover(
                                    bytes32 domainSeparator,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s,
                                    bytes memory typeHashAndData
                                ) internal pure returns (address) {
                                    bytes32 digest = keccak256(
                                        abi.encodePacked(
                                            "\x19\x01",
                                            domainSeparator,
                                            keccak256(typeHashAndData)
                                        )
                                    );
                                    return ECRecover.recover(digest, v, r, s);
                                }
                            }
                            
                            // File: contracts/v2/EIP712Domain.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title EIP712 Domain
                             */
                            contract EIP712Domain {
                                /**
                                 * @dev EIP712 Domain Separator
                                 */
                                bytes32 public DOMAIN_SEPARATOR;
                            }
                            
                            // File: contracts/v2/EIP3009.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title EIP-3009
                             * @notice Provide internal implementation for gas-abstracted transfers
                             * @dev Contracts that inherit from this must wrap these with publicly
                             * accessible functions, optionally adding modifiers where necessary
                             */
                            abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain {
                                // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
                                bytes32
                                    public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
                            
                                // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
                                bytes32
                                    public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
                            
                                // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
                                bytes32
                                    public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
                            
                                /**
                                 * @dev authorizer address => nonce => bool (true if nonce is used)
                                 */
                                mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
                            
                                event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
                                event AuthorizationCanceled(
                                    address indexed authorizer,
                                    bytes32 indexed nonce
                                );
                            
                                /**
                                 * @notice Returns the state of an authorization
                                 * @dev Nonces are randomly generated 32-byte data unique to the
                                 * authorizer's address
                                 * @param authorizer    Authorizer's address
                                 * @param nonce         Nonce of the authorization
                                 * @return True if the nonce is used
                                 */
                                function authorizationState(address authorizer, bytes32 nonce)
                                    external
                                    view
                                    returns (bool)
                                {
                                    return _authorizationStates[authorizer][nonce];
                                }
                            
                                /**
                                 * @notice Execute a transfer with a signed authorization
                                 * @param from          Payer's address (Authorizer)
                                 * @param to            Payee's address
                                 * @param value         Amount to be transferred
                                 * @param validAfter    The time after which this is valid (unix time)
                                 * @param validBefore   The time before which this is valid (unix time)
                                 * @param nonce         Unique nonce
                                 * @param v             v of the signature
                                 * @param r             r of the signature
                                 * @param s             s of the signature
                                 */
                                function _transferWithAuthorization(
                                    address from,
                                    address to,
                                    uint256 value,
                                    uint256 validAfter,
                                    uint256 validBefore,
                                    bytes32 nonce,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) internal {
                                    _requireValidAuthorization(from, nonce, validAfter, validBefore);
                            
                                    bytes memory data = abi.encode(
                                        TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                                        from,
                                        to,
                                        value,
                                        validAfter,
                                        validBefore,
                                        nonce
                                    );
                                    require(
                                        EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from,
                                        "FiatTokenV2: invalid signature"
                                    );
                            
                                    _markAuthorizationAsUsed(from, nonce);
                                    _transfer(from, to, value);
                                }
                            
                                /**
                                 * @notice Receive a transfer with a signed authorization from the payer
                                 * @dev This has an additional check to ensure that the payee's address
                                 * matches the caller of this function to prevent front-running attacks.
                                 * @param from          Payer's address (Authorizer)
                                 * @param to            Payee's address
                                 * @param value         Amount to be transferred
                                 * @param validAfter    The time after which this is valid (unix time)
                                 * @param validBefore   The time before which this is valid (unix time)
                                 * @param nonce         Unique nonce
                                 * @param v             v of the signature
                                 * @param r             r of the signature
                                 * @param s             s of the signature
                                 */
                                function _receiveWithAuthorization(
                                    address from,
                                    address to,
                                    uint256 value,
                                    uint256 validAfter,
                                    uint256 validBefore,
                                    bytes32 nonce,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) internal {
                                    require(to == msg.sender, "FiatTokenV2: caller must be the payee");
                                    _requireValidAuthorization(from, nonce, validAfter, validBefore);
                            
                                    bytes memory data = abi.encode(
                                        RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
                                        from,
                                        to,
                                        value,
                                        validAfter,
                                        validBefore,
                                        nonce
                                    );
                                    require(
                                        EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from,
                                        "FiatTokenV2: invalid signature"
                                    );
                            
                                    _markAuthorizationAsUsed(from, nonce);
                                    _transfer(from, to, value);
                                }
                            
                                /**
                                 * @notice Attempt to cancel an authorization
                                 * @param authorizer    Authorizer's address
                                 * @param nonce         Nonce of the authorization
                                 * @param v             v of the signature
                                 * @param r             r of the signature
                                 * @param s             s of the signature
                                 */
                                function _cancelAuthorization(
                                    address authorizer,
                                    bytes32 nonce,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) internal {
                                    _requireUnusedAuthorization(authorizer, nonce);
                            
                                    bytes memory data = abi.encode(
                                        CANCEL_AUTHORIZATION_TYPEHASH,
                                        authorizer,
                                        nonce
                                    );
                                    require(
                                        EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == authorizer,
                                        "FiatTokenV2: invalid signature"
                                    );
                            
                                    _authorizationStates[authorizer][nonce] = true;
                                    emit AuthorizationCanceled(authorizer, nonce);
                                }
                            
                                /**
                                 * @notice Check that an authorization is unused
                                 * @param authorizer    Authorizer's address
                                 * @param nonce         Nonce of the authorization
                                 */
                                function _requireUnusedAuthorization(address authorizer, bytes32 nonce)
                                    private
                                    view
                                {
                                    require(
                                        !_authorizationStates[authorizer][nonce],
                                        "FiatTokenV2: authorization is used or canceled"
                                    );
                                }
                            
                                /**
                                 * @notice Check that authorization is valid
                                 * @param authorizer    Authorizer's address
                                 * @param nonce         Nonce of the authorization
                                 * @param validAfter    The time after which this is valid (unix time)
                                 * @param validBefore   The time before which this is valid (unix time)
                                 */
                                function _requireValidAuthorization(
                                    address authorizer,
                                    bytes32 nonce,
                                    uint256 validAfter,
                                    uint256 validBefore
                                ) private view {
                                    require(
                                        now > validAfter,
                                        "FiatTokenV2: authorization is not yet valid"
                                    );
                                    require(now < validBefore, "FiatTokenV2: authorization is expired");
                                    _requireUnusedAuthorization(authorizer, nonce);
                                }
                            
                                /**
                                 * @notice Mark an authorization as used
                                 * @param authorizer    Authorizer's address
                                 * @param nonce         Nonce of the authorization
                                 */
                                function _markAuthorizationAsUsed(address authorizer, bytes32 nonce)
                                    private
                                {
                                    _authorizationStates[authorizer][nonce] = true;
                                    emit AuthorizationUsed(authorizer, nonce);
                                }
                            }
                            
                            // File: contracts/v2/EIP2612.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title EIP-2612
                             * @notice Provide internal implementation for gas-abstracted approvals
                             */
                            abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain {
                                // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
                                bytes32
                                    public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                            
                                mapping(address => uint256) private _permitNonces;
                            
                                /**
                                 * @notice Nonces for permit
                                 * @param owner Token owner's address (Authorizer)
                                 * @return Next nonce
                                 */
                                function nonces(address owner) external view returns (uint256) {
                                    return _permitNonces[owner];
                                }
                            
                                /**
                                 * @notice Verify a signed approval permit and execute if valid
                                 * @param owner     Token owner's address (Authorizer)
                                 * @param spender   Spender's address
                                 * @param value     Amount of allowance
                                 * @param deadline  The time at which this expires (unix time)
                                 * @param v         v of the signature
                                 * @param r         r of the signature
                                 * @param s         s of the signature
                                 */
                                function _permit(
                                    address owner,
                                    address spender,
                                    uint256 value,
                                    uint256 deadline,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) internal {
                                    require(deadline >= now, "FiatTokenV2: permit is expired");
                            
                                    bytes memory data = abi.encode(
                                        PERMIT_TYPEHASH,
                                        owner,
                                        spender,
                                        value,
                                        _permitNonces[owner]++,
                                        deadline
                                    );
                                    require(
                                        EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner,
                                        "EIP2612: invalid signature"
                                    );
                            
                                    _approve(owner, spender, value);
                                }
                            }
                            
                            // File: contracts/v2/FiatTokenV2.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            /**
                             * @title FiatToken V2
                             * @notice ERC20 Token backed by fiat reserves, version 2
                             */
                            contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 {
                                uint8 internal _initializedVersion;
                            
                                /**
                                 * @notice Initialize v2
                                 * @param newName   New token name
                                 */
                                function initializeV2(string calldata newName) external {
                                    // solhint-disable-next-line reason-string
                                    require(initialized && _initializedVersion == 0);
                                    name = newName;
                                    DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(newName, "2");
                                    _initializedVersion = 1;
                                }
                            
                                /**
                                 * @notice Increase the allowance by a given increment
                                 * @param spender   Spender's address
                                 * @param increment Amount of increase in allowance
                                 * @return True if successful
                                 */
                                function increaseAllowance(address spender, uint256 increment)
                                    external
                                    whenNotPaused
                                    notBlacklisted(msg.sender)
                                    notBlacklisted(spender)
                                    returns (bool)
                                {
                                    _increaseAllowance(msg.sender, spender, increment);
                                    return true;
                                }
                            
                                /**
                                 * @notice Decrease the allowance by a given decrement
                                 * @param spender   Spender's address
                                 * @param decrement Amount of decrease in allowance
                                 * @return True if successful
                                 */
                                function decreaseAllowance(address spender, uint256 decrement)
                                    external
                                    whenNotPaused
                                    notBlacklisted(msg.sender)
                                    notBlacklisted(spender)
                                    returns (bool)
                                {
                                    _decreaseAllowance(msg.sender, spender, decrement);
                                    return true;
                                }
                            
                                /**
                                 * @notice Execute a transfer with a signed authorization
                                 * @param from          Payer's address (Authorizer)
                                 * @param to            Payee's address
                                 * @param value         Amount to be transferred
                                 * @param validAfter    The time after which this is valid (unix time)
                                 * @param validBefore   The time before which this is valid (unix time)
                                 * @param nonce         Unique nonce
                                 * @param v             v of the signature
                                 * @param r             r of the signature
                                 * @param s             s of the signature
                                 */
                                function transferWithAuthorization(
                                    address from,
                                    address to,
                                    uint256 value,
                                    uint256 validAfter,
                                    uint256 validBefore,
                                    bytes32 nonce,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                                    _transferWithAuthorization(
                                        from,
                                        to,
                                        value,
                                        validAfter,
                                        validBefore,
                                        nonce,
                                        v,
                                        r,
                                        s
                                    );
                                }
                            
                                /**
                                 * @notice Receive a transfer with a signed authorization from the payer
                                 * @dev This has an additional check to ensure that the payee's address
                                 * matches the caller of this function to prevent front-running attacks.
                                 * @param from          Payer's address (Authorizer)
                                 * @param to            Payee's address
                                 * @param value         Amount to be transferred
                                 * @param validAfter    The time after which this is valid (unix time)
                                 * @param validBefore   The time before which this is valid (unix time)
                                 * @param nonce         Unique nonce
                                 * @param v             v of the signature
                                 * @param r             r of the signature
                                 * @param s             s of the signature
                                 */
                                function receiveWithAuthorization(
                                    address from,
                                    address to,
                                    uint256 value,
                                    uint256 validAfter,
                                    uint256 validBefore,
                                    bytes32 nonce,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                                    _receiveWithAuthorization(
                                        from,
                                        to,
                                        value,
                                        validAfter,
                                        validBefore,
                                        nonce,
                                        v,
                                        r,
                                        s
                                    );
                                }
                            
                                /**
                                 * @notice Attempt to cancel an authorization
                                 * @dev Works only if the authorization is not yet used.
                                 * @param authorizer    Authorizer's address
                                 * @param nonce         Nonce of the authorization
                                 * @param v             v of the signature
                                 * @param r             r of the signature
                                 * @param s             s of the signature
                                 */
                                function cancelAuthorization(
                                    address authorizer,
                                    bytes32 nonce,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external whenNotPaused {
                                    _cancelAuthorization(authorizer, nonce, v, r, s);
                                }
                            
                                /**
                                 * @notice Update allowance with a signed permit
                                 * @param owner       Token owner's address (Authorizer)
                                 * @param spender     Spender's address
                                 * @param value       Amount of allowance
                                 * @param deadline    Expiration time, seconds since the epoch
                                 * @param v           v of the signature
                                 * @param r           r of the signature
                                 * @param s           s of the signature
                                 */
                                function permit(
                                    address owner,
                                    address spender,
                                    uint256 value,
                                    uint256 deadline,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external whenNotPaused notBlacklisted(owner) notBlacklisted(spender) {
                                    _permit(owner, spender, value, deadline, v, r, s);
                                }
                            
                                /**
                                 * @notice Internal function to increase the allowance by a given increment
                                 * @param owner     Token owner's address
                                 * @param spender   Spender's address
                                 * @param increment Amount of increase
                                 */
                                function _increaseAllowance(
                                    address owner,
                                    address spender,
                                    uint256 increment
                                ) internal override {
                                    _approve(owner, spender, allowed[owner][spender].add(increment));
                                }
                            
                                /**
                                 * @notice Internal function to decrease the allowance by a given decrement
                                 * @param owner     Token owner's address
                                 * @param spender   Spender's address
                                 * @param decrement Amount of decrease
                                 */
                                function _decreaseAllowance(
                                    address owner,
                                    address spender,
                                    uint256 decrement
                                ) internal override {
                                    _approve(
                                        owner,
                                        spender,
                                        allowed[owner][spender].sub(
                                            decrement,
                                            "ERC20: decreased allowance below zero"
                                        )
                                    );
                                }
                            }
                            
                            // File: contracts/v2/FiatTokenV2_1.sol
                            
                            /**
                             * Copyright (c) 2018-2020 CENTRE SECZ
                             *
                             * Permission is hereby granted, free of charge, to any person obtaining a copy
                             * of this software and associated documentation files (the "Software"), to deal
                             * in the Software without restriction, including without limitation the rights
                             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                             * copies of the Software, and to permit persons to whom the Software is
                             * furnished to do so, subject to the following conditions:
                             *
                             * The above copyright notice and this permission notice shall be included in
                             * copies or substantial portions of the Software.
                             *
                             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                             * SOFTWARE.
                             */
                            
                            pragma solidity 0.6.12;
                            
                            // solhint-disable func-name-mixedcase
                            
                            /**
                             * @title FiatToken V2.1
                             * @notice ERC20 Token backed by fiat reserves, version 2.1
                             */
                            contract FiatTokenV2_1 is FiatTokenV2 {
                                /**
                                 * @notice Initialize v2.1
                                 * @param lostAndFound  The address to which the locked funds are sent
                                 */
                                function initializeV2_1(address lostAndFound) external {
                                    // solhint-disable-next-line reason-string
                                    require(_initializedVersion == 1);
                            
                                    uint256 lockedAmount = balances[address(this)];
                                    if (lockedAmount > 0) {
                                        _transfer(address(this), lostAndFound, lockedAmount);
                                    }
                                    blacklisted[address(this)] = true;
                            
                                    _initializedVersion = 2;
                                }
                            
                                /**
                                 * @notice Version string for the EIP712 domain separator
                                 * @return Version string
                                 */
                                function version() external view returns (string memory) {
                                    return "2";
                                }
                            }

                            File 5 of 8: BeaconProxy
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)
                            pragma solidity ^0.8.0;
                            import "./IBeacon.sol";
                            import "../Proxy.sol";
                            import "../ERC1967/ERC1967Upgrade.sol";
                            /**
                             * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
                             *
                             * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
                             * conflict with the storage layout of the implementation behind the proxy.
                             *
                             * _Available since v3.4._
                             */
                            contract BeaconProxy is Proxy, ERC1967Upgrade {
                                /**
                                 * @dev Initializes the proxy with `beacon`.
                                 *
                                 * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                                 * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
                                 * constructor.
                                 *
                                 * Requirements:
                                 *
                                 * - `beacon` must be a contract with the interface {IBeacon}.
                                 */
                                constructor(address beacon, bytes memory data) payable {
                                    _upgradeBeaconToAndCall(beacon, data, false);
                                }
                                /**
                                 * @dev Returns the current beacon address.
                                 */
                                function _beacon() internal view virtual returns (address) {
                                    return _getBeacon();
                                }
                                /**
                                 * @dev Returns the current implementation address of the associated beacon.
                                 */
                                function _implementation() internal view virtual override returns (address) {
                                    return IBeacon(_getBeacon()).implementation();
                                }
                                /**
                                 * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                                 *
                                 * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                                 *
                                 * Requirements:
                                 *
                                 * - `beacon` must be a contract.
                                 * - The implementation returned by `beacon` must be a contract.
                                 */
                                function _setBeacon(address beacon, bytes memory data) internal virtual {
                                    _upgradeBeaconToAndCall(beacon, data, false);
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev This is the interface that {BeaconProxy} expects of its beacon.
                             */
                            interface IBeacon {
                                /**
                                 * @dev Must return an address that can be used as a delegate call target.
                                 *
                                 * {BeaconProxy} will check that this address is a contract.
                                 */
                                function implementation() external view returns (address);
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
                             * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
                             * be specified by overriding the virtual {_implementation} function.
                             *
                             * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
                             * different contract through the {_delegate} function.
                             *
                             * The success and return data of the delegated call will be returned back to the caller of the proxy.
                             */
                            abstract contract Proxy {
                                /**
                                 * @dev Delegates the current call to `implementation`.
                                 *
                                 * This function does not return to its internal call site, it will return directly to the external caller.
                                 */
                                function _delegate(address implementation) internal virtual {
                                    assembly {
                                        // Copy msg.data. We take full control of memory in this inline assembly
                                        // block because it will not return to Solidity code. We overwrite the
                                        // Solidity scratch pad at memory position 0.
                                        calldatacopy(0, 0, calldatasize())
                                        // Call the implementation.
                                        // out and outsize are 0 because we don't know the size yet.
                                        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                                        // Copy the returned data.
                                        returndatacopy(0, 0, returndatasize())
                                        switch result
                                        // delegatecall returns 0 on error.
                                        case 0 {
                                            revert(0, returndatasize())
                                        }
                                        default {
                                            return(0, returndatasize())
                                        }
                                    }
                                }
                                /**
                                 * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
                                 * and {_fallback} should delegate.
                                 */
                                function _implementation() internal view virtual returns (address);
                                /**
                                 * @dev Delegates the current call to the address returned by `_implementation()`.
                                 *
                                 * This function does not return to its internal call site, it will return directly to the external caller.
                                 */
                                function _fallback() internal virtual {
                                    _beforeFallback();
                                    _delegate(_implementation());
                                }
                                /**
                                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                                 * function in the contract matches the call data.
                                 */
                                fallback() external payable virtual {
                                    _fallback();
                                }
                                /**
                                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                                 * is empty.
                                 */
                                receive() external payable virtual {
                                    _fallback();
                                }
                                /**
                                 * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                                 * call, or as part of the Solidity `fallback` or `receive` functions.
                                 *
                                 * If overridden should call `super._beforeFallback()`.
                                 */
                                function _beforeFallback() internal virtual {}
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                            pragma solidity ^0.8.2;
                            import "../beacon/IBeacon.sol";
                            import "../../interfaces/draft-IERC1822.sol";
                            import "../../utils/Address.sol";
                            import "../../utils/StorageSlot.sol";
                            /**
                             * @dev This abstract contract provides getters and event emitting update functions for
                             * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                             *
                             * _Available since v4.1._
                             *
                             * @custom:oz-upgrades-unsafe-allow delegatecall
                             */
                            abstract contract ERC1967Upgrade {
                                // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                                bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                                /**
                                 * @dev Storage slot with the address of the current implementation.
                                 * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                                 * validated in the constructor.
                                 */
                                bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                                /**
                                 * @dev Emitted when the implementation is upgraded.
                                 */
                                event Upgraded(address indexed implementation);
                                /**
                                 * @dev Returns the current implementation address.
                                 */
                                function _getImplementation() internal view returns (address) {
                                    return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                                }
                                /**
                                 * @dev Stores a new address in the EIP1967 implementation slot.
                                 */
                                function _setImplementation(address newImplementation) private {
                                    require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                    StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                                }
                                /**
                                 * @dev Perform implementation upgrade
                                 *
                                 * Emits an {Upgraded} event.
                                 */
                                function _upgradeTo(address newImplementation) internal {
                                    _setImplementation(newImplementation);
                                    emit Upgraded(newImplementation);
                                }
                                /**
                                 * @dev Perform implementation upgrade with additional setup call.
                                 *
                                 * Emits an {Upgraded} event.
                                 */
                                function _upgradeToAndCall(
                                    address newImplementation,
                                    bytes memory data,
                                    bool forceCall
                                ) internal {
                                    _upgradeTo(newImplementation);
                                    if (data.length > 0 || forceCall) {
                                        Address.functionDelegateCall(newImplementation, data);
                                    }
                                }
                                /**
                                 * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                                 *
                                 * Emits an {Upgraded} event.
                                 */
                                function _upgradeToAndCallUUPS(
                                    address newImplementation,
                                    bytes memory data,
                                    bool forceCall
                                ) internal {
                                    // Upgrades from old implementations will perform a rollback test. This test requires the new
                                    // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                                    // this special case will break upgrade paths from old UUPS implementation to new ones.
                                    if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                                        _setImplementation(newImplementation);
                                    } else {
                                        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                                            require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                                        } catch {
                                            revert("ERC1967Upgrade: new implementation is not UUPS");
                                        }
                                        _upgradeToAndCall(newImplementation, data, forceCall);
                                    }
                                }
                                /**
                                 * @dev Storage slot with the admin of the contract.
                                 * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                                 * validated in the constructor.
                                 */
                                bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                                /**
                                 * @dev Emitted when the admin account has changed.
                                 */
                                event AdminChanged(address previousAdmin, address newAdmin);
                                /**
                                 * @dev Returns the current admin.
                                 */
                                function _getAdmin() internal view returns (address) {
                                    return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                                }
                                /**
                                 * @dev Stores a new address in the EIP1967 admin slot.
                                 */
                                function _setAdmin(address newAdmin) private {
                                    require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                    StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                                }
                                /**
                                 * @dev Changes the admin of the proxy.
                                 *
                                 * Emits an {AdminChanged} event.
                                 */
                                function _changeAdmin(address newAdmin) internal {
                                    emit AdminChanged(_getAdmin(), newAdmin);
                                    _setAdmin(newAdmin);
                                }
                                /**
                                 * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                                 * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                                 */
                                bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                                /**
                                 * @dev Emitted when the beacon is upgraded.
                                 */
                                event BeaconUpgraded(address indexed beacon);
                                /**
                                 * @dev Returns the current beacon.
                                 */
                                function _getBeacon() internal view returns (address) {
                                    return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                                }
                                /**
                                 * @dev Stores a new beacon in the EIP1967 beacon slot.
                                 */
                                function _setBeacon(address newBeacon) private {
                                    require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                                    require(
                                        Address.isContract(IBeacon(newBeacon).implementation()),
                                        "ERC1967: beacon implementation is not a contract"
                                    );
                                    StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                                }
                                /**
                                 * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                                 * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                                 *
                                 * Emits a {BeaconUpgraded} event.
                                 */
                                function _upgradeBeaconToAndCall(
                                    address newBeacon,
                                    bytes memory data,
                                    bool forceCall
                                ) internal {
                                    _setBeacon(newBeacon);
                                    emit BeaconUpgraded(newBeacon);
                                    if (data.length > 0 || forceCall) {
                                        Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
                             * proxy whose upgrades are fully controlled by the current implementation.
                             */
                            interface IERC1822Proxiable {
                                /**
                                 * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                                 * address.
                                 *
                                 * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                                 * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                                 * function revert if invoked through a proxy.
                                 */
                                function proxiableUUID() external view returns (bytes32);
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
                            pragma solidity ^0.8.1;
                            /**
                             * @dev Collection of functions related to the address type
                             */
                            library Address {
                                /**
                                 * @dev Returns true if `account` is a contract.
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * It is unsafe to assume that an address for which this function returns
                                 * false is an externally-owned account (EOA) and not a contract.
                                 *
                                 * Among others, `isContract` will return false for the following
                                 * types of addresses:
                                 *
                                 *  - an externally-owned account
                                 *  - a contract in construction
                                 *  - an address where a contract will be created
                                 *  - an address where a contract lived, but was destroyed
                                 * ====
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                                 *
                                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                                 * constructor.
                                 * ====
                                 */
                                function isContract(address account) internal view returns (bool) {
                                    // This method relies on extcodesize/address.code.length, which returns 0
                                    // for contracts in construction, since the code is only stored at the end
                                    // of the constructor execution.
                                    return account.code.length > 0;
                                }
                                /**
                                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                                 * `recipient`, forwarding all available gas and reverting on errors.
                                 *
                                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                                 * imposed by `transfer`, making them unable to receive funds via
                                 * `transfer`. {sendValue} removes this limitation.
                                 *
                                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                                 *
                                 * IMPORTANT: because control is transferred to `recipient`, care must be
                                 * taken to not create reentrancy vulnerabilities. Consider using
                                 * {ReentrancyGuard} or the
                                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                                 */
                                function sendValue(address payable recipient, uint256 amount) internal {
                                    require(address(this).balance >= amount, "Address: insufficient balance");
                                    (bool success, ) = recipient.call{value: amount}("");
                                    require(success, "Address: unable to send value, recipient may have reverted");
                                }
                                /**
                                 * @dev Performs a Solidity function call using a low level `call`. A
                                 * plain `call` is an unsafe replacement for a function call: use this
                                 * function instead.
                                 *
                                 * If `target` reverts with a revert reason, it is bubbled up by this
                                 * function (like regular Solidity function calls).
                                 *
                                 * Returns the raw returned data. To convert to the expected return value,
                                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                                 *
                                 * Requirements:
                                 *
                                 * - `target` must be a contract.
                                 * - calling `target` with `data` must not revert.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionCall(target, data, "Address: low-level call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                                 * `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, 0, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but also transferring `value` wei to `target`.
                                 *
                                 * Requirements:
                                 *
                                 * - the calling contract must have an ETH balance of at least `value`.
                                 * - the called Solidity function must be `payable`.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value
                                ) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    require(address(this).balance >= value, "Address: insufficient balance for call");
                                    require(isContract(target), "Address: call to non-contract");
                                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                    return functionStaticCall(target, data, "Address: low-level static call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal view returns (bytes memory) {
                                    require(isContract(target), "Address: static call to non-contract");
                                    (bool success, bytes memory returndata) = target.staticcall(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a delegate call.
                                 *
                                 * _Available since v3.4._
                                 */
                                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a delegate call.
                                 *
                                 * _Available since v3.4._
                                 */
                                function functionDelegateCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    require(isContract(target), "Address: delegate call to non-contract");
                                    (bool success, bytes memory returndata) = target.delegatecall(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                                 * revert reason using the provided one.
                                 *
                                 * _Available since v4.3._
                                 */
                                function verifyCallResult(
                                    bool success,
                                    bytes memory returndata,
                                    string memory errorMessage
                                ) internal pure returns (bytes memory) {
                                    if (success) {
                                        return returndata;
                                    } else {
                                        // Look for revert reason and bubble it up if present
                                        if (returndata.length > 0) {
                                            // The easiest way to bubble the revert reason is using memory via assembly
                                            /// @solidity memory-safe-assembly
                                            assembly {
                                                let returndata_size := mload(returndata)
                                                revert(add(32, returndata), returndata_size)
                                            }
                                        } else {
                                            revert(errorMessage);
                                        }
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Library for reading and writing primitive types to specific storage slots.
                             *
                             * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
                             * This library helps with reading and writing to such slots without the need for inline assembly.
                             *
                             * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
                             *
                             * Example usage to set ERC1967 implementation slot:
                             * ```
                             * contract ERC1967 {
                             *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                             *
                             *     function _getImplementation() internal view returns (address) {
                             *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                             *     }
                             *
                             *     function _setImplementation(address newImplementation) internal {
                             *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                             *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                             *     }
                             * }
                             * ```
                             *
                             * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                             */
                            library StorageSlot {
                                struct AddressSlot {
                                    address value;
                                }
                                struct BooleanSlot {
                                    bool value;
                                }
                                struct Bytes32Slot {
                                    bytes32 value;
                                }
                                struct Uint256Slot {
                                    uint256 value;
                                }
                                /**
                                 * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                                 */
                                function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        r.slot := slot
                                    }
                                }
                                /**
                                 * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                                 */
                                function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        r.slot := slot
                                    }
                                }
                                /**
                                 * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                                 */
                                function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        r.slot := slot
                                    }
                                }
                                /**
                                 * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                                 */
                                function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        r.slot := slot
                                    }
                                }
                            }
                            

                            File 6 of 8: UpgradeableBeacon
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)
                            pragma solidity ^0.8.0;
                            import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
                            import "@openzeppelin/contracts/utils/Address.sol";
                            import "@openzeppelin/contracts/access/Ownable.sol";
                            /**
                             * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
                             * implementation contract, which is where they will delegate all function calls.
                             *
                             * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
                             */
                            contract UpgradeableBeacon is IBeacon, Ownable {
                                address private _implementation;
                                /**
                                 * @dev Emitted when the implementation returned by the beacon is changed.
                                 */
                                event Upgraded(address indexed implementation);
                                /**
                                 * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
                                 * beacon.
                                 */
                                constructor(address implementation_) {
                                    _setImplementation(implementation_);
                                }
                                /**
                                 * @dev Returns the current implementation address.
                                 */
                                function implementation() public view virtual override returns (address) {
                                    return _implementation;
                                }
                                /**
                                 * @dev Upgrades the beacon to a new implementation.
                                 *
                                 * Emits an {Upgraded} event.
                                 *
                                 * Requirements:
                                 *
                                 * - msg.sender must be the owner of the contract.
                                 * - `newImplementation` must be a contract.
                                 */
                                function upgradeTo(address newImplementation) public virtual onlyOwner {
                                    _setImplementation(newImplementation);
                                    emit Upgraded(newImplementation);
                                }
                                /**
                                 * @dev Sets the implementation contract address for this beacon
                                 *
                                 * Requirements:
                                 *
                                 * - `newImplementation` must be a contract.
                                 */
                                function _setImplementation(address newImplementation) private {
                                    require(
                                        Address.isContract(newImplementation),
                                        "UpgradeableBeacon: implementation is not a contract"
                                    );
                                    _implementation = newImplementation;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev This is the interface that {BeaconProxy} expects of its beacon.
                             */
                            interface IBeacon {
                                /**
                                 * @dev Must return an address that can be used as a delegate call target.
                                 *
                                 * {BeaconProxy} will check that this address is a contract.
                                 */
                                function implementation() external view returns (address);
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
                            pragma solidity ^0.8.1;
                            /**
                             * @dev Collection of functions related to the address type
                             */
                            library Address {
                                /**
                                 * @dev Returns true if `account` is a contract.
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * It is unsafe to assume that an address for which this function returns
                                 * false is an externally-owned account (EOA) and not a contract.
                                 *
                                 * Among others, `isContract` will return false for the following
                                 * types of addresses:
                                 *
                                 *  - an externally-owned account
                                 *  - a contract in construction
                                 *  - an address where a contract will be created
                                 *  - an address where a contract lived, but was destroyed
                                 * ====
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                                 *
                                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                                 * constructor.
                                 * ====
                                 */
                                function isContract(address account) internal view returns (bool) {
                                    // This method relies on extcodesize/address.code.length, which returns 0
                                    // for contracts in construction, since the code is only stored at the end
                                    // of the constructor execution.
                                    return account.code.length > 0;
                                }
                                /**
                                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                                 * `recipient`, forwarding all available gas and reverting on errors.
                                 *
                                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                                 * imposed by `transfer`, making them unable to receive funds via
                                 * `transfer`. {sendValue} removes this limitation.
                                 *
                                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                                 *
                                 * IMPORTANT: because control is transferred to `recipient`, care must be
                                 * taken to not create reentrancy vulnerabilities. Consider using
                                 * {ReentrancyGuard} or the
                                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                                 */
                                function sendValue(address payable recipient, uint256 amount) internal {
                                    require(address(this).balance >= amount, "Address: insufficient balance");
                                    (bool success, ) = recipient.call{value: amount}("");
                                    require(success, "Address: unable to send value, recipient may have reverted");
                                }
                                /**
                                 * @dev Performs a Solidity function call using a low level `call`. A
                                 * plain `call` is an unsafe replacement for a function call: use this
                                 * function instead.
                                 *
                                 * If `target` reverts with a revert reason, it is bubbled up by this
                                 * function (like regular Solidity function calls).
                                 *
                                 * Returns the raw returned data. To convert to the expected return value,
                                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                                 *
                                 * Requirements:
                                 *
                                 * - `target` must be a contract.
                                 * - calling `target` with `data` must not revert.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionCall(target, data, "Address: low-level call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                                 * `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, 0, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but also transferring `value` wei to `target`.
                                 *
                                 * Requirements:
                                 *
                                 * - the calling contract must have an ETH balance of at least `value`.
                                 * - the called Solidity function must be `payable`.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value
                                ) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    require(address(this).balance >= value, "Address: insufficient balance for call");
                                    require(isContract(target), "Address: call to non-contract");
                                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                    return functionStaticCall(target, data, "Address: low-level static call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal view returns (bytes memory) {
                                    require(isContract(target), "Address: static call to non-contract");
                                    (bool success, bytes memory returndata) = target.staticcall(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a delegate call.
                                 *
                                 * _Available since v3.4._
                                 */
                                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a delegate call.
                                 *
                                 * _Available since v3.4._
                                 */
                                function functionDelegateCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    require(isContract(target), "Address: delegate call to non-contract");
                                    (bool success, bytes memory returndata) = target.delegatecall(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                                 * revert reason using the provided one.
                                 *
                                 * _Available since v4.3._
                                 */
                                function verifyCallResult(
                                    bool success,
                                    bytes memory returndata,
                                    string memory errorMessage
                                ) internal pure returns (bytes memory) {
                                    if (success) {
                                        return returndata;
                                    } else {
                                        // Look for revert reason and bubble it up if present
                                        if (returndata.length > 0) {
                                            // The easiest way to bubble the revert reason is using memory via assembly
                                            /// @solidity memory-safe-assembly
                                            assembly {
                                                let returndata_size := mload(returndata)
                                                revert(add(32, returndata), returndata_size)
                                            }
                                        } else {
                                            revert(errorMessage);
                                        }
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
                            pragma solidity ^0.8.0;
                            import "../utils/Context.sol";
                            /**
                             * @dev Contract module which provides a basic access control mechanism, where
                             * there is an account (an owner) that can be granted exclusive access to
                             * specific functions.
                             *
                             * By default, the owner account will be the one that deploys the contract. This
                             * can later be changed with {transferOwnership}.
                             *
                             * This module is used through inheritance. It will make available the modifier
                             * `onlyOwner`, which can be applied to your functions to restrict their use to
                             * the owner.
                             */
                            abstract contract Ownable is Context {
                                address private _owner;
                                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                                /**
                                 * @dev Initializes the contract setting the deployer as the initial owner.
                                 */
                                constructor() {
                                    _transferOwnership(_msgSender());
                                }
                                /**
                                 * @dev Throws if called by any account other than the owner.
                                 */
                                modifier onlyOwner() {
                                    _checkOwner();
                                    _;
                                }
                                /**
                                 * @dev Returns the address of the current owner.
                                 */
                                function owner() public view virtual returns (address) {
                                    return _owner;
                                }
                                /**
                                 * @dev Throws if the sender is not the owner.
                                 */
                                function _checkOwner() internal view virtual {
                                    require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                }
                                /**
                                 * @dev Leaves the contract without owner. It will not be possible to call
                                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                                 *
                                 * NOTE: Renouncing ownership will leave the contract without an owner,
                                 * thereby removing any functionality that is only available to the owner.
                                 */
                                function renounceOwnership() public virtual onlyOwner {
                                    _transferOwnership(address(0));
                                }
                                /**
                                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                                 * Can only be called by the current owner.
                                 */
                                function transferOwnership(address newOwner) public virtual onlyOwner {
                                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                                    _transferOwnership(newOwner);
                                }
                                /**
                                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                                 * Internal function without access restriction.
                                 */
                                function _transferOwnership(address newOwner) internal virtual {
                                    address oldOwner = _owner;
                                    _owner = newOwner;
                                    emit OwnershipTransferred(oldOwner, newOwner);
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Provides information about the current execution context, including the
                             * sender of the transaction and its data. While these are generally available
                             * via msg.sender and msg.data, they should not be accessed in such a direct
                             * manner, since when dealing with meta-transactions the account sending and
                             * paying for execution may not be the actual sender (as far as an application
                             * is concerned).
                             *
                             * This contract is only required for intermediate, library-like contracts.
                             */
                            abstract contract Context {
                                function _msgSender() internal view virtual returns (address) {
                                    return msg.sender;
                                }
                                function _msgData() internal view virtual returns (bytes calldata) {
                                    return msg.data;
                                }
                            }
                            

                            File 7 of 8: PoolMaster
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "./abstract/PoolMetadata.sol";
                            import "./abstract/PoolRewards.sol";
                            import "./abstract/PoolConfiguration.sol";
                            /// @notice This is perimary protocol contract, describing borrowing Pool
                            contract PoolMaster is PoolRewards, PoolConfiguration, PoolMetadata {
                                // CONSTRUCTOR
                                /// @notice Upgradeable contract constructor
                                /// @param manager_ Address of the Pool's manager
                                /// @param currency_ Address of the currency token
                                function initialize(address manager_, IERC20Upgradeable currency_)
                                    public
                                    initializer
                                {
                                    __PoolBaseInfo_init(manager_, currency_);
                                }
                                // VERSION
                                function version() external pure virtual returns (string memory) {
                                    return "1.1.0";
                                }
                                // OVERRIDES
                                /// @notice Override of the mint function, see {IERC20-_mint}
                                function _mint(address account, uint256 amount)
                                    internal
                                    override(ERC20Upgradeable, PoolRewards)
                                {
                                    super._mint(account, amount);
                                }
                                /// @notice Override of the mint function, see {IERC20-_burn}
                                function _burn(address account, uint256 amount)
                                    internal
                                    override(ERC20Upgradeable, PoolRewards)
                                {
                                    super._burn(account, amount);
                                }
                                /// @notice Override of the transfer function, see {IERC20-_transfer}
                                function _transfer(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) internal override(ERC20Upgradeable, PoolRewards) {
                                    super._transfer(from, to, amount);
                                }
                                /// @notice Override of the decimals function, see {IERC20Metadata-decimals}
                                /// @return r-token decimals
                                function decimals()
                                    public
                                    view
                                    override(ERC20Upgradeable, PoolMetadata)
                                    returns (uint8)
                                {
                                    return super.decimals();
                                }
                                /// @notice Override of the decimals function, see {IERC20Metadata-symbol}
                                /// @return Pool's symbol
                                function symbol()
                                    public
                                    view
                                    override(ERC20Upgradeable, PoolMetadata)
                                    returns (string memory)
                                {
                                    return super.symbol();
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
                            import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
                            import "./PoolBase.sol";
                            import "../libraries/Decimal.sol";
                            /// @notice This contract describes Pool's external view functions for observing it's current state
                            abstract contract PoolMetadata is PoolBase {
                                using Decimal for uint256;
                                /// @notice Function that returns cp-token decimals
                                /// @return Cp-token decimals
                                function decimals() public view virtual override returns (uint8) {
                                    return IERC20MetadataUpgradeable(address(currency)).decimals();
                                }
                                /// @notice Function returns pool's symbol
                                /// @return Pool's symbol
                                function symbol() public view virtual override returns (string memory) {
                                    return _symbol;
                                }
                                /// @notice Function returns current (with accrual) pool state
                                /// @return Current state
                                function state() external view returns (State) {
                                    return _state(_accrueInterestVirtual());
                                }
                                /// @notice Function returns current (with accrual) last accrual timestamp
                                /// @return Last accrual timestamp
                                function lastAccrual() external view returns (uint256) {
                                    return _accrueInterestVirtual().lastAccrual;
                                }
                                /// @notice Function returns current (with accrual) interest value
                                /// @return Current interest
                                function interest() external view returns (uint256) {
                                    return _interest(_accrueInterestVirtual());
                                }
                                /// @notice Function returns current (with accrual) amount of funds available to LP for withdrawal
                                /// @return Current available to withdraw funds
                                function availableToWithdraw() external view returns (uint256) {
                                    if (debtClaimed) {
                                        return cash();
                                    } else {
                                        BorrowInfo memory info = _accrueInterestVirtual();
                                        return
                                            MathUpgradeable.min(
                                                _availableToProviders(info),
                                                _availableProvisionalDefault(info)
                                            );
                                    }
                                }
                                /// @notice Function returns current (with accrual) amount of funds available for manager to borrow
                                /// @return Current available to borrow funds
                                function availableToBorrow() external view returns (uint256) {
                                    return _availableToBorrow(_accrueInterestVirtual());
                                }
                                /// @notice Function returns current (with accrual) pool size
                                /// @return Current pool size
                                function poolSize() external view returns (uint256) {
                                    return _poolSize(_accrueInterestVirtual());
                                }
                                /// @notice Function returns current principal value
                                /// @return Current principal
                                function principal() external view returns (uint256) {
                                    return _info.principal;
                                }
                                /// @notice Function returns current (with accrual) total borrows value
                                /// @return Current borrows
                                function borrows() external view returns (uint256) {
                                    return _accrueInterestVirtual().borrows;
                                }
                                /// @notice Function returns current (with accrual) reserves value
                                /// @return Current reserves
                                function reserves() public view returns (uint256) {
                                    return _accrueInterestVirtual().reserves;
                                }
                                /// @notice Function returns current (with accrual) insurance value
                                /// @return Current insurance
                                function insurance() external view returns (uint256) {
                                    return _accrueInterestVirtual().insurance;
                                }
                                /// @notice Function returns timestamp when pool entered zero utilization state (0 if didn't enter)
                                /// @return Timestamp of entering zero utilization
                                function enteredZeroUtilization() external view returns (uint256) {
                                    return _info.enteredZeroUtilization;
                                }
                                /// @notice Function returns timestamp when pool entered warning utilization state (0 if didn't enter)
                                /// @return Timestamp of entering warning utilization
                                function enteredProvisionalDefault() external view returns (uint256) {
                                    return _accrueInterestVirtual().enteredProvisionalDefault;
                                }
                                /// @notice Function returns current (with accrual) exchange rate of rTokens for currency tokens
                                /// @return Current exchange rate as 10-digits decimal
                                function getCurrentExchangeRate() external view returns (uint256) {
                                    if (totalSupply() == 0) {
                                        return Decimal.ONE;
                                    } else if (debtClaimed) {
                                        return cash().divDecimal(totalSupply());
                                    } else {
                                        BorrowInfo memory info = _accrueInterestVirtual();
                                        return
                                            (_availableToProviders(info) + info.borrows).divDecimal(
                                                totalSupply()
                                            );
                                    }
                                }
                                /// @notice Function to get current borrow interest rate
                                /// @return Borrow interest rate as 18-digit decimal
                                function getBorrowRate() public view returns (uint256) {
                                    BorrowInfo memory info = _accrueInterestVirtual();
                                    return
                                        interestRateModel.getBorrowRate(
                                            cash(),
                                            info.borrows,
                                            info.reserves + info.insurance + (info.borrows - info.principal)
                                        );
                                }
                                /// @notice Function to get current supply interest rate
                                /// @return Supply interest rate as 18-digit decimal
                                function getSupplyRate() external view returns (uint256) {
                                    BorrowInfo memory info = _accrueInterestVirtual();
                                    return
                                        interestRateModel.getSupplyRate(
                                            cash(),
                                            info.borrows,
                                            info.reserves +
                                                info.insurance +
                                                (info.borrows - info.principal),
                                            reserveFactor + insuranceFactor
                                        );
                                }
                                /// @notice Function to get current utilization rate
                                /// @return Utilization rate as 18-digit decimal
                                function getUtilizationRate() external view returns (uint256) {
                                    BorrowInfo memory info = _accrueInterestVirtual();
                                    return
                                        interestRateModel.utilizationRate(
                                            cash(),
                                            info.borrows,
                                            info.insurance + info.reserves + _interest(info)
                                        );
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
                            import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
                            import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
                            import "./PoolBase.sol";
                            /// @notice This contract describes everything related to Pool's reward logic
                            abstract contract PoolRewards is PoolBase {
                                using SafeCastUpgradeable for uint256;
                                using SafeCastUpgradeable for int256;
                                using SafeERC20Upgradeable for IERC20Upgradeable;
                                /// @notice Amount of RBN rewards per second for liquidity providers in this pool
                                uint256 public rewardPerSecond;
                                /// @notice Value by which all rewards are magnified for calculation
                                uint256 internal constant REWARD_MAGNITUDE = 2**128;
                                /// @notice Timestamp when last staking reward distribution occured
                                uint256 internal _lastRewardDistribution;
                                /// @notice Reward per LP token, magnified by 2**128 for increased precision
                                uint256 internal _magnifiedRewardPerShare;
                                /// @notice Reward corrections of accounts (to remain previous rewards unchanged when user's balance changes)
                                mapping(address => int256) internal _magnifiedRewardCorrections;
                                /// @notice Reward withdrawals of accounts
                                mapping(address => uint256) internal _withdrawals;
                                // EVENTS
                                /// @notice Event emitted when account withdraws his reward
                                /// @param account Account who withdraws reward
                                /// @param amount Amount of reward tokens withdrawn
                                event RewardWithdrawn(address indexed account, uint256 amount);
                                /// @notice Event emitted when new reward per block is set
                                /// @param newRewardPerSecond New amount of reward tokens distirbuted per second
                                event RewardPerSecondSet(uint256 newRewardPerSecond);
                                // PUBLIC FUNCTIONS
                                /// @notice Function is called through Factory to withdraw reward for some user
                                /// @param account Account to withdraw reward for
                                /// @return Withdrawn amount
                                function withdrawReward(address account)
                                    external
                                    onlyFactory
                                    returns (uint256)
                                {
                                    _accrueInterest();
                                    _distributeReward();
                                    uint256 withdrawable = withdrawableRewardOf(account);
                                    if (withdrawable > 0) {
                                        _withdrawals[account] += withdrawable;
                                        emit RewardWithdrawn(account, withdrawable);
                                    }
                                    return withdrawable;
                                }
                                /// @notice Function is called by Factory to set new reward speed per second
                                /// @param rewardPerSecond_ New reward per second
                                function setRewardPerSecond(uint256 rewardPerSecond_) external onlyFactory {
                                    _accrueInterest();
                                    _distributeReward();
                                    if (_lastRewardDistribution == 0) {
                                        _lastRewardDistribution = _info.lastAccrual;
                                    }
                                    rewardPerSecond = rewardPerSecond_;
                                    emit RewardPerSecondSet(rewardPerSecond_);
                                }
                                // VIEW FUNCTIONS
                                /// @notice Gets total accumulated reward of some account
                                /// @return Total accumulated reward of account
                                function accumulativeRewardOf(address account)
                                    public
                                    view
                                    returns (uint256)
                                {
                                    BorrowInfo memory info = _accrueInterestVirtual();
                                    uint256 currentRewardPerShare = _magnifiedRewardPerShare;
                                    if (
                                        _lastRewardDistribution != 0 &&
                                        info.lastAccrual > _lastRewardDistribution &&
                                        totalSupply() > 0
                                    ) {
                                        uint256 period = info.lastAccrual - _lastRewardDistribution;
                                        currentRewardPerShare +=
                                            (REWARD_MAGNITUDE * period * rewardPerSecond) /
                                            totalSupply();
                                    }
                                    return
                                        ((balanceOf(account) * currentRewardPerShare).toInt256() +
                                            _magnifiedRewardCorrections[account]).toUint256() /
                                        REWARD_MAGNITUDE;
                                }
                                /// @notice Gets withdrawn part of reward of some account
                                /// @return Withdrawn reward of account
                                function withdrawnRewardOf(address account) public view returns (uint256) {
                                    return _withdrawals[account];
                                }
                                /// @notice Gets currently withdrawable reward of some account
                                /// @return Withdrawable reward of account
                                function withdrawableRewardOf(address account)
                                    public
                                    view
                                    returns (uint256)
                                {
                                    return accumulativeRewardOf(account) - withdrawnRewardOf(account);
                                }
                                // INTERNAL FUNCTIONS
                                /// @notice Internal function for rewards distribution
                                function _distributeReward() internal {
                                    if (
                                        rewardPerSecond > 0 &&
                                        _lastRewardDistribution != 0 &&
                                        _info.lastAccrual > _lastRewardDistribution &&
                                        totalSupply() > 0
                                    ) {
                                        uint256 period = _info.lastAccrual - _lastRewardDistribution;
                                        _magnifiedRewardPerShare +=
                                            (REWARD_MAGNITUDE * period * rewardPerSecond) /
                                            totalSupply();
                                    }
                                    _lastRewardDistribution = _info.lastAccrual;
                                }
                                /// @notice Override of mint function with rewards corrections
                                /// @param account Account to mint for
                                /// @param amount Amount to mint
                                function _mint(address account, uint256 amount) internal virtual override {
                                    _distributeReward();
                                    super._mint(account, amount);
                                    _magnifiedRewardCorrections[account] -= (_magnifiedRewardPerShare *
                                        amount).toInt256();
                                }
                                /// @notice Override of burn function with rewards corrections
                                /// @param account Account to burn from
                                /// @param amount Amount to burn
                                function _burn(address account, uint256 amount) internal virtual override {
                                    _distributeReward();
                                    super._burn(account, amount);
                                    _magnifiedRewardCorrections[account] += (_magnifiedRewardPerShare *
                                        amount).toInt256();
                                }
                                /// @notice Override of transfer function with rewards corrections
                                /// @param from Account to transfer from
                                /// @param to Account to transfer to
                                /// @param amount Amount to transfer
                                function _transfer(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) internal virtual override {
                                    _accrueInterest();
                                    _distributeReward();
                                    super._transfer(from, to, amount);
                                    _magnifiedRewardCorrections[from] += (_magnifiedRewardPerShare * amount)
                                        .toInt256();
                                    _magnifiedRewardCorrections[to] -= (_magnifiedRewardPerShare * amount)
                                        .toInt256();
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "./PoolBase.sol";
                            /// @notice This contract describes pool's configuration functions
                            abstract contract PoolConfiguration is PoolBase {
                                /// @notice Function is used to update pool's manager (only called through factory)
                                /// @param manager_ New manager of the pool
                                function setManager(address manager_) external onlyFactory {
                                    require(manager_ != address(0), "AIZ");
                                    manager = manager_;
                                }
                                /// @notice Function is used to update pool's interest rate model (only called by governor)
                                /// @param interestRateModel_ New IRM of the pool
                                function setInterestRateModel(IInterestRateModel interestRateModel_)
                                    external
                                    onlyGovernor
                                {
                                    require(address(interestRateModel_) != address(0), "AIZ");
                                    _accrueInterest();
                                    interestRateModel = interestRateModel_;
                                }
                                /// @notice Function is used to update pool's reserve factor (only called by governor)
                                /// @param reserveFactor_ New reserve factor of the pool
                                function setReserveFactor(uint256 reserveFactor_) external onlyGovernor {
                                    require(reserveFactor + insuranceFactor <= Decimal.ONE, "GTO");
                                    reserveFactor = reserveFactor_;
                                }
                                /// @notice Function is used to update pool's insurance factor (only called by governor)
                                /// @param insuranceFactor_ New insurance factor of the pool
                                function setInsuranceFactor(uint256 insuranceFactor_)
                                    external
                                    onlyGovernor
                                {
                                    require(reserveFactor + insuranceFactor <= Decimal.ONE, "GTO");
                                    insuranceFactor = insuranceFactor_;
                                }
                                /// @notice Function is used to update pool's warning utilization (only called by governor)
                                /// @param warningUtilization_ New warning utilization of the pool
                                function setWarningUtilization(uint256 warningUtilization_)
                                    external
                                    onlyGovernor
                                {
                                    require(warningUtilization <= Decimal.ONE, "GTO");
                                    _accrueInterest();
                                    warningUtilization = warningUtilization_;
                                    _checkUtilization();
                                }
                                /// @notice Function is used to update pool's provisional default utilization (only called by governor)
                                /// @param provisionalDefaultUtilization_ New provisional default utilization of the pool
                                function setProvisionalDefaultUtilization(
                                    uint256 provisionalDefaultUtilization_
                                ) external onlyGovernor {
                                    require(provisionalDefaultUtilization_ <= Decimal.ONE, "GTO");
                                    _accrueInterest();
                                    provisionalDefaultUtilization = provisionalDefaultUtilization_;
                                    _checkUtilization();
                                }
                                /// @notice Function is used to update pool's warning grace period (only called by governor)
                                /// @param warningGracePeriod_ New warning grace period of the pool
                                function setWarningGracePeriod(uint256 warningGracePeriod_)
                                    external
                                    onlyGovernor
                                {
                                    _accrueInterest();
                                    warningGracePeriod = warningGracePeriod_;
                                    _checkUtilization();
                                }
                                /// @notice Function is used to update pool's max inactive period (only called by governor)
                                /// @param maxInactivePeriod_ New max inactive period of the pool
                                function setMaxInactivePeriod(uint256 maxInactivePeriod_)
                                    external
                                    onlyGovernor
                                {
                                    _accrueInterest();
                                    maxInactivePeriod = maxInactivePeriod_;
                                }
                                /// @notice Function is used to update pool's period to start auction (only called by governor)
                                /// @param periodToStartAuction_ New period to start auction of the pool
                                function setPeriodToStartAuction(uint256 periodToStartAuction_)
                                    external
                                    onlyGovernor
                                {
                                    periodToStartAuction = periodToStartAuction_;
                                }
                                /// @notice Function is used to update pool's symbol (only called by governor)
                                /// @param symbol_ New symbol of the pool
                                function setSymbol(string memory symbol_) external onlyGovernor {
                                    _symbol = symbol_;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Standard math utilities missing in the Solidity language.
                             */
                            library MathUpgradeable {
                                enum Rounding {
                                    Down, // Toward negative infinity
                                    Up, // Toward infinity
                                    Zero // Toward zero
                                }
                                /**
                                 * @dev Returns the largest of two numbers.
                                 */
                                function max(uint256 a, uint256 b) internal pure returns (uint256) {
                                    return a >= b ? a : b;
                                }
                                /**
                                 * @dev Returns the smallest of two numbers.
                                 */
                                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                                    return a < b ? a : b;
                                }
                                /**
                                 * @dev Returns the average of two numbers. The result is rounded towards
                                 * zero.
                                 */
                                function average(uint256 a, uint256 b) internal pure returns (uint256) {
                                    // (a + b) / 2 can overflow.
                                    return (a & b) + (a ^ b) / 2;
                                }
                                /**
                                 * @dev Returns the ceiling of the division of two numbers.
                                 *
                                 * This differs from standard division with `/` in that it rounds up instead
                                 * of rounding down.
                                 */
                                function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                                    // (a + b - 1) / b can overflow on addition, so we distribute.
                                    return a == 0 ? 0 : (a - 1) / b + 1;
                                }
                                /**
                                 * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                                 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                                 * with further edits by Uniswap Labs also under MIT license.
                                 */
                                function mulDiv(
                                    uint256 x,
                                    uint256 y,
                                    uint256 denominator
                                ) internal pure returns (uint256 result) {
                                    unchecked {
                                        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                        // variables such that product = prod1 * 2^256 + prod0.
                                        uint256 prod0; // Least significant 256 bits of the product
                                        uint256 prod1; // Most significant 256 bits of the product
                                        assembly {
                                            let mm := mulmod(x, y, not(0))
                                            prod0 := mul(x, y)
                                            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                        }
                                        // Handle non-overflow cases, 256 by 256 division.
                                        if (prod1 == 0) {
                                            return prod0 / denominator;
                                        }
                                        // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                        require(denominator > prod1);
                                        ///////////////////////////////////////////////
                                        // 512 by 256 division.
                                        ///////////////////////////////////////////////
                                        // Make division exact by subtracting the remainder from [prod1 prod0].
                                        uint256 remainder;
                                        assembly {
                                            // Compute remainder using mulmod.
                                            remainder := mulmod(x, y, denominator)
                                            // Subtract 256 bit number from 512 bit number.
                                            prod1 := sub(prod1, gt(remainder, prod0))
                                            prod0 := sub(prod0, remainder)
                                        }
                                        // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                        // See https://cs.stackexchange.com/q/138556/92363.
                                        // Does not overflow because the denominator cannot be zero at this stage in the function.
                                        uint256 twos = denominator & (~denominator + 1);
                                        assembly {
                                            // Divide denominator by twos.
                                            denominator := div(denominator, twos)
                                            // Divide [prod1 prod0] by twos.
                                            prod0 := div(prod0, twos)
                                            // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                                            twos := add(div(sub(0, twos), twos), 1)
                                        }
                                        // Shift in bits from prod1 into prod0.
                                        prod0 |= prod1 * twos;
                                        // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                        // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                        // four bits. That is, denominator * inv = 1 mod 2^4.
                                        uint256 inverse = (3 * denominator) ^ 2;
                                        // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                        // in modular arithmetic, doubling the correct bits in each step.
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                        // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                        // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                        // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                        // is no longer required.
                                        result = prod0 * inverse;
                                        return result;
                                    }
                                }
                                /**
                                 * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                                 */
                                function mulDiv(
                                    uint256 x,
                                    uint256 y,
                                    uint256 denominator,
                                    Rounding rounding
                                ) internal pure returns (uint256) {
                                    uint256 result = mulDiv(x, y, denominator);
                                    if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                                        result += 1;
                                    }
                                    return result;
                                }
                                /**
                                 * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down.
                                 *
                                 * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                                 */
                                function sqrt(uint256 a) internal pure returns (uint256) {
                                    if (a == 0) {
                                        return 0;
                                    }
                                    // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                                    // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                                    // `msb(a) <= a < 2*msb(a)`.
                                    // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
                                    // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
                                    // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
                                    // good first aproximation of `sqrt(a)` with at least 1 correct bit.
                                    uint256 result = 1;
                                    uint256 x = a;
                                    if (x >> 128 > 0) {
                                        x >>= 128;
                                        result <<= 64;
                                    }
                                    if (x >> 64 > 0) {
                                        x >>= 64;
                                        result <<= 32;
                                    }
                                    if (x >> 32 > 0) {
                                        x >>= 32;
                                        result <<= 16;
                                    }
                                    if (x >> 16 > 0) {
                                        x >>= 16;
                                        result <<= 8;
                                    }
                                    if (x >> 8 > 0) {
                                        x >>= 8;
                                        result <<= 4;
                                    }
                                    if (x >> 4 > 0) {
                                        x >>= 4;
                                        result <<= 2;
                                    }
                                    if (x >> 2 > 0) {
                                        result <<= 1;
                                    }
                                    // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                                    // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                                    // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                                    // into the expected uint128 result.
                                    unchecked {
                                        result = (result + a / result) >> 1;
                                        result = (result + a / result) >> 1;
                                        result = (result + a / result) >> 1;
                                        result = (result + a / result) >> 1;
                                        result = (result + a / result) >> 1;
                                        result = (result + a / result) >> 1;
                                        result = (result + a / result) >> 1;
                                        return min(result, a / result);
                                    }
                                }
                                /**
                                 * @notice Calculates sqrt(a), following the selected rounding direction.
                                 */
                                function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                                    uint256 result = sqrt(a);
                                    if (rounding == Rounding.Up && result * result < a) {
                                        result += 1;
                                    }
                                    return result;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
                            pragma solidity ^0.8.0;
                            import "../IERC20Upgradeable.sol";
                            /**
                             * @dev Interface for the optional metadata functions from the ERC20 standard.
                             *
                             * _Available since v4.1._
                             */
                            interface IERC20MetadataUpgradeable is IERC20Upgradeable {
                                /**
                                 * @dev Returns the name of the token.
                                 */
                                function name() external view returns (string memory);
                                /**
                                 * @dev Returns the symbol of the token.
                                 */
                                function symbol() external view returns (string memory);
                                /**
                                 * @dev Returns the decimals places of the token.
                                 */
                                function decimals() external view returns (uint8);
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
                            import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
                            import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol";
                            import "./PoolBaseInfo.sol";
                            import "../libraries/Decimal.sol";
                            import "../interfaces/IAuction.sol";
                            /// @notice This contract describes basic logic of the Pool - everything related to borrowing
                            abstract contract PoolBase is PoolBaseInfo {
                                using SafeERC20Upgradeable for IERC20Upgradeable;
                                using Decimal for uint256;
                                // PUBLIC FUNCTIONS
                                /// @notice Function is used to provide liquidity for Pool in exchange for rTokens
                                /// @dev Approval for desired amount of currency token should be given in prior
                                /// @param currencyAmount Amount of currency token that user want to provide
                                /// @param referral Optional referral address
                                function provide(uint256 currencyAmount, address referral) external {
                                    _provide(currencyAmount, referral);
                                }
                                /// @notice Function is used to provide liquidity for Pool in exchange for rTokens, using EIP2612 off-chain signed permit for currency
                                /// @param currencyAmount Amount of currency token that user want to provide
                                /// @param referral Optional referral address
                                /// @param deadline Deadline for EIP2612 approval
                                /// @param v V component of permit signature
                                /// @param r R component of permit signature
                                /// @param s S component of permit signature
                                function provideWithPermit(
                                    uint256 currencyAmount,
                                    address referral,
                                    uint256 deadline,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external {
                                    IERC20PermitUpgradeable(address(currency)).permit(
                                        msg.sender,
                                        address(this),
                                        currencyAmount,
                                        deadline,
                                        v,
                                        r,
                                        s
                                    );
                                    _provide(currencyAmount, referral);
                                }
                                /// @notice Function is used to redeem previously provided liquidity with interest, burning rTokens
                                /// @param tokens Amount of rTokens to burn (MaxUint256 to burn maximal possible)
                                function redeem(uint256 tokens) external {
                                    _accrueInterest();
                                    uint256 exchangeRate = _storedExchangeRate();
                                    uint256 currencyAmount;
                                    if (tokens == type(uint256).max) {
                                        (tokens, currencyAmount) = _maxWithdrawable(exchangeRate);
                                    } else {
                                        currencyAmount = tokens.mulDecimal(exchangeRate);
                                    }
                                    _redeem(tokens, currencyAmount);
                                }
                                /// @notice Function is used to redeem previously provided liquidity with interest, burning rTokens
                                /// @param currencyAmount Amount of currency to redeem (MaxUint256 to redeem maximal possible)
                                function redeemCurrency(uint256 currencyAmount) external {
                                    _accrueInterest();
                                    uint256 exchangeRate = _storedExchangeRate();
                                    uint256 tokens;
                                    if (currencyAmount == type(uint256).max) {
                                        (tokens, currencyAmount) = _maxWithdrawable(exchangeRate);
                                    } else {
                                        tokens = currencyAmount.divDecimal(exchangeRate);
                                    }
                                    _redeem(tokens, currencyAmount);
                                }
                                /// @notice Function is used to borrow from the pool
                                /// @param amount Amount of currency to borrow (MaxUint256 to borrow everything available)
                                /// @param receiver Address where to transfer currency
                                function borrow(uint256 amount, address receiver)
                                    external
                                    onlyManager
                                    onlyActiveAccrual
                                {
                                    if (amount == type(uint256).max) {
                                        amount = _availableToBorrow(_info);
                                    } else {
                                        require(amount <= _availableToBorrow(_info), "NEL");
                                    }
                                    require(amount > 0, "CBZ");
                                    _info.principal += amount;
                                    _info.borrows += amount;
                                    _transferOut(receiver, amount);
                                    _checkUtilization();
                                    emit Borrowed(amount, receiver);
                                }
                                /// @notice Function is used to repay borrowed funds
                                /// @param amount Amount to repay (MaxUint256 to repay all debt)
                                /// @param closeNow True to close pool immedeately
                                function repay(uint256 amount, bool closeNow)
                                    external
                                    onlyManager
                                    onlyActiveAccrual
                                {
                                    if (amount == type(uint256).max) {
                                        amount = _info.borrows;
                                    } else {
                                        require(amount <= _info.borrows, "MTB");
                                    }
                                    _transferIn(msg.sender, amount);
                                    if (amount > _info.borrows - _info.principal) {
                                        _info.principal -= amount - (_info.borrows - _info.principal);
                                    }
                                    _info.borrows -= amount;
                                    _checkUtilization();
                                    emit Repaid(amount);
                                    if (closeNow) {
                                        require(_info.borrows == 0, "BNZ");
                                        _close();
                                    }
                                }
                                /// @notice Function is used to close pool
                                function close() external {
                                    _accrueInterest();
                                    address governor = factory.owner();
                                    address debtOwner = ownerOfDebt();
                                    bool managerClosing = _info.borrows == 0 && msg.sender == manager;
                                    bool inactiveOverMax = _info.enteredZeroUtilization != 0 &&
                                        block.timestamp > _info.enteredZeroUtilization + maxInactivePeriod;
                                    bool governorClosing = msg.sender == governor &&
                                        (inactiveOverMax || debtOwner != address(0));
                                    bool ownerOfDebtClosing = msg.sender == debtOwner;
                                    require(managerClosing || governorClosing || ownerOfDebtClosing, "SCC");
                                    _close();
                                }
                                /// @notice Function is used to distribute insurance and close pool after period to start auction passed
                                function allowWithdrawalAfterNoAuction() external {
                                    _accrueInterest();
                                    bool isDefaulting = _state(_info) == State.Default;
                                    bool auctionNotStarted = IAuction(factory.auction()).state(
                                        address(this)
                                    ) == IAuction.State.NotStarted;
                                    bool periodToStartPassed = block.timestamp >=
                                        _info.lastAccrual + periodToStartAuction;
                                    require(
                                        isDefaulting && auctionNotStarted && periodToStartPassed,
                                        "CDC"
                                    );
                                    _info.insurance = 0;
                                    debtClaimed = true;
                                    _close();
                                }
                                /// @notice Function is called by governor to transfer reserves to the treasury
                                function transferReserves() external onlyGovernor {
                                    _accrueInterest();
                                    _transferReserves();
                                }
                                /// @notice Function is called by governor to force pool default (in case of default in other chain)
                                function forceDefault() external onlyGovernor onlyActiveAccrual {
                                    _info.state = State.Default;
                                }
                                /// @notice Function is called by Auction contract when auction is started
                                function processAuctionStart() external onlyAuction {
                                    _accrueInterest();
                                    _transferReserves();
                                    factory.burnStake();
                                }
                                /// @notice Function is called by Auction contract to process pool debt claim
                                function processDebtClaim() external onlyAuction {
                                    _accrueInterest();
                                    _info.state = State.Default;
                                    address debtOwner = ownerOfDebt();
                                    if (_info.insurance > 0) {
                                        _transferOut(debtOwner, _info.insurance);
                                        _info.insurance = 0;
                                    }
                                    debtClaimed = true;
                                }
                                // INTERNAL FUNCTIONS
                                /// @notice Internal function that processes providing liquidity for Pool in exchange for rTokens
                                /// @param currencyAmount Amount of currency token that user want to provide
                                /// @param referral Optional referral address
                                function _provide(uint256 currencyAmount, address referral) internal onlyActiveAccrual {
                                    uint256 exchangeRate = _storedExchangeRate();
                                    _transferIn(msg.sender, currencyAmount);
                                    uint256 tokens = currencyAmount.divDecimal(exchangeRate);
                                    _mint(msg.sender, tokens);
                                    _checkUtilization();
                                    emit Provided(msg.sender, referral, currencyAmount, tokens);
                                }
                                /// @notice Internal function that processes token redemption
                                /// @param tokensAmount Amount of tokens being redeemed
                                /// @param currencyAmount Equivalent amount of currency
                                function _redeem(uint256 tokensAmount, uint256 currencyAmount) internal {
                                    if (debtClaimed) {
                                        require(currencyAmount <= cash(), "NEC");
                                    } else {
                                        require(
                                            currencyAmount <= _availableToProviders(_info) &&
                                                currencyAmount <= _availableProvisionalDefault(_info),
                                            "NEC"
                                        );
                                    }
                                    _burn(msg.sender, tokensAmount);
                                    _transferOut(msg.sender, currencyAmount);
                                    if (!debtClaimed) {
                                        _checkUtilization();
                                    }
                                    emit Redeemed(msg.sender, currencyAmount, tokensAmount);
                                }
                                /// @notice Internal function to transfer reserves to the treasury
                                function _transferReserves() internal {
                                    _transferOut(factory.treasury(), _info.reserves);
                                    _info.reserves = 0;
                                }
                                /// @notice Internal function for closing pool
                                function _close() internal {
                                    require(_info.state != State.Closed, "PIC");
                                    _info.state = State.Closed;
                                    _transferReserves();
                                    if (_info.insurance > 0) {
                                        _transferOut(manager, _info.insurance);
                                        _info.insurance = 0;
                                    }
                                    factory.closePool();
                                    emit Closed();
                                }
                                /// @notice Internal function to accrue interest
                                function _accrueInterest() internal {
                                    _info = _accrueInterestVirtual();
                                }
                                /// @notice Internal function that is called at each action to check for zero/warning/default utilization
                                function _checkUtilization() internal {
                                    if (_info.borrows == 0) {
                                        _info.enteredProvisionalDefault = 0;
                                        if (_info.enteredZeroUtilization == 0) {
                                            _info.enteredZeroUtilization = block.timestamp;
                                        }
                                        return;
                                    }
                                    _info.enteredZeroUtilization = 0;
                                    if (_info.borrows >= _poolSize(_info).mulDecimal(warningUtilization)) {
                                        if (
                                            _info.enteredProvisionalDefault == 0 &&
                                            _info.borrows >=
                                            _poolSize(_info).mulDecimal(provisionalDefaultUtilization)
                                        ) {
                                            _info.enteredProvisionalDefault = block.timestamp;
                                        }
                                    } else {
                                        _info.enteredProvisionalDefault = 0;
                                    }
                                }
                                function _transferIn(address from, uint256 amount) internal virtual {
                                    currency.safeTransferFrom(from, address(this), amount);
                                }
                                function _transferOut(address to, uint256 amount) internal virtual {
                                    currency.safeTransfer(to, amount);
                                }
                                // PUBLIC VIEW
                                /// @notice Function to get owner of the pool's debt
                                /// @return Pool's debt owner
                                function ownerOfDebt() public view returns (address) {
                                    return IAuction(factory.auction()).ownerOfDebt(address(this));
                                }
                                /// @notice Function returns cash amount (balance of currency in the pool)
                                /// @return Cash amount
                                function cash() public view virtual returns (uint256) {
                                    return currency.balanceOf(address(this));
                                }
                                // INTERNAL VIEW
                                /// @notice Function to get current pool state
                                /// @return Pool state as State enumerable
                                function _state(BorrowInfo memory info) internal view returns (State) {
                                    if (info.state == State.Closed || info.state == State.Default) {
                                        return info.state;
                                    }
                                    if (info.enteredProvisionalDefault != 0) {
                                        if (
                                            block.timestamp >=
                                            info.enteredProvisionalDefault + warningGracePeriod
                                        ) {
                                            return State.Default;
                                        } else {
                                            return State.ProvisionalDefault;
                                        }
                                    }
                                    if (
                                        info.borrows > 0 &&
                                        info.borrows >= _poolSize(info).mulDecimal(warningUtilization)
                                    ) {
                                        return State.Warning;
                                    }
                                    return info.state;
                                }
                                /// @notice Function returns interest value for given borrow info
                                /// @param info Borrow info struct
                                /// @return Interest for given info
                                function _interest(BorrowInfo memory info) internal pure returns (uint256) {
                                    return info.borrows - info.principal;
                                }
                                /// @notice Function returns amount of funds generally available for providers value for given borrow info
                                /// @param info Borrow info struct
                                /// @return Available to providers for given info
                                function _availableToProviders(BorrowInfo memory info)
                                    internal
                                    view
                                    returns (uint256)
                                {
                                    return cash() - info.reserves - info.insurance;
                                }
                                /// @notice Function returns available to borrow value for given borrow info
                                /// @param info Borrow info struct
                                /// @return Available to borrow for given info
                                function _availableToBorrow(BorrowInfo memory info)
                                    internal
                                    view
                                    returns (uint256)
                                {
                                    uint256 basicAvailable = _availableToProviders(info) - _interest(info);
                                    uint256 borrowsForWarning = _poolSize(info).mulDecimal(
                                        warningUtilization
                                    );
                                    if (borrowsForWarning > info.borrows) {
                                        return
                                            MathUpgradeable.min(
                                                borrowsForWarning - info.borrows,
                                                basicAvailable
                                            );
                                    } else {
                                        return 0;
                                    }
                                }
                                /// @notice Function returns pool size for given borrow info
                                /// @param info Borrow info struct
                                /// @return Pool size for given info
                                function _poolSize(BorrowInfo memory info) internal view returns (uint256) {
                                    return _availableToProviders(info) + info.principal;
                                }
                                /// @notice Function returns funds available to be taken from pool before provisional default will be reached
                                /// @param info Borrow info struct
                                /// @return Pool size for given info
                                function _availableProvisionalDefault(BorrowInfo memory info)
                                    internal
                                    view
                                    returns (uint256)
                                {
                                    if (provisionalDefaultUtilization == 0) {
                                        return 0;
                                    }
                                    uint256 poolSizeForProvisionalDefault = info.borrows.divDecimal(
                                        provisionalDefaultUtilization
                                    );
                                    uint256 currentPoolSize = _poolSize(info);
                                    return
                                        currentPoolSize > poolSizeForProvisionalDefault
                                            ? currentPoolSize - poolSizeForProvisionalDefault
                                            : 0;
                                }
                                /// @notice Function returns maximal redeemable amount for given exchange rate
                                /// @param exchangeRate Exchange rate of r-tokens to currency
                                /// @return tokensAmount Maximal redeemable amount of tokens
                                /// @return currencyAmount Maximal redeemable amount of currency
                                function _maxWithdrawable(uint256 exchangeRate)
                                    internal
                                    view
                                    returns (uint256 tokensAmount, uint256 currencyAmount)
                                {
                                    currencyAmount = _availableToProviders(_info);
                                    if (!debtClaimed) {
                                        uint256 availableProvisionalDefault = _availableProvisionalDefault(
                                            _info
                                        );
                                        if (availableProvisionalDefault < currencyAmount) {
                                            currencyAmount = availableProvisionalDefault;
                                        }
                                    }
                                    tokensAmount = currencyAmount.divDecimal(exchangeRate);
                                    if (balanceOf(msg.sender) < tokensAmount) {
                                        tokensAmount = balanceOf(msg.sender);
                                        currencyAmount = tokensAmount.mulDecimal(exchangeRate);
                                    }
                                }
                                /// @notice Function returns stored (without accruing) exchange rate of rTokens for currency tokens
                                /// @return Stored exchange rate as 10-digits decimal
                                function _storedExchangeRate() internal view returns (uint256) {
                                    if (totalSupply() == 0) {
                                        return Decimal.ONE;
                                    } else if (debtClaimed) {
                                        return cash().divDecimal(totalSupply());
                                    } else {
                                        return
                                            (_availableToProviders(_info) + _info.borrows).divDecimal(
                                                totalSupply()
                                            );
                                    }
                                }
                                /// @notice Function returns timestamp when pool entered or will enter provisional default at given interest rate
                                /// @param interestRate Borrows interest rate at current period
                                /// @return Timestamp of entering provisional default (0 if won't ever enter)
                                function _entranceOfProvisionalDefault(uint256 interestRate)
                                    internal
                                    view
                                    returns (uint256)
                                {
                                    if (_info.enteredProvisionalDefault != 0) {
                                        return _info.enteredProvisionalDefault;
                                    }
                                    if (_info.borrows == 0 || interestRate == 0) {
                                        return 0;
                                    }
                                    // Consider:
                                    // IFPD - Interest for provisional default
                                    // PSPD = Pool size at provisional default
                                    // IRPD = Reserves & insurance at provisional default
                                    // IR = Current reserves and insurance
                                    // PDU = Provisional default utilization
                                    // We have: Borrows + IFPD = PDU * PSPD
                                    // => Borrows + IFPD = PDU * (Principal + Cash + IRPD)
                                    // => Borrows + IFPD = PDU * (Principal + Cash + IR + IFPD * (insuranceFactor + reserveFactor))
                                    // => IFPD * (1 + PDU * (reserveFactor + insuranceFactor)) = PDU * PoolSize - Borrows
                                    // => IFPD = (PDU * PoolSize - Borrows) / (1 + PDU * (reserveFactor + insuranceFactor))
                                    uint256 numerator = _poolSize(_info).mulDecimal(
                                        provisionalDefaultUtilization
                                    ) - _info.borrows;
                                    uint256 denominator = Decimal.ONE +
                                        provisionalDefaultUtilization.mulDecimal(
                                            reserveFactor + insuranceFactor
                                        );
                                    uint256 interestForProvisionalDefault = numerator.divDecimal(
                                        denominator
                                    );
                                    uint256 interestPerSec = _info.borrows * interestRate;
                                    // Time delta is calculated as interest for provisional default divided by interest per sec (rounded up)
                                    uint256 timeDelta = (interestForProvisionalDefault *
                                        Decimal.ONE +
                                        interestPerSec -
                                        1) / interestPerSec;
                                    uint256 entrance = _info.lastAccrual + timeDelta;
                                    return entrance <= block.timestamp ? entrance : 0;
                                }
                                /// @notice Function virtually accrues interest and returns updated borrow info struct
                                /// @return Borrow info struct after accrual
                                function _accrueInterestVirtual()
                                    internal
                                    view
                                    returns (BorrowInfo memory)
                                {
                                    BorrowInfo memory newInfo = _info;
                                    if (
                                        block.timestamp == newInfo.lastAccrual ||
                                        newInfo.state == State.Default ||
                                        newInfo.state == State.Closed
                                    ) {
                                        return newInfo;
                                    }
                                    uint256 interestRate = interestRateModel.getBorrowRate(
                                        cash(),
                                        newInfo.borrows,
                                        newInfo.reserves + newInfo.insurance + _interest(newInfo)
                                    );
                                    newInfo.lastAccrual = block.timestamp;
                                    newInfo.enteredProvisionalDefault = _entranceOfProvisionalDefault(
                                        interestRate
                                    );
                                    if (
                                        newInfo.enteredProvisionalDefault != 0 &&
                                        newInfo.enteredProvisionalDefault + warningGracePeriod <
                                        newInfo.lastAccrual
                                    ) {
                                        newInfo.lastAccrual =
                                            newInfo.enteredProvisionalDefault +
                                            warningGracePeriod;
                                    }
                                    uint256 interestDelta = newInfo.borrows.mulDecimal(
                                        interestRate * (newInfo.lastAccrual - _info.lastAccrual)
                                    );
                                    uint256 reservesDelta = interestDelta.mulDecimal(reserveFactor);
                                    uint256 insuranceDelta = interestDelta.mulDecimal(insuranceFactor);
                                    if (
                                        newInfo.borrows + interestDelta + reservesDelta + insuranceDelta >
                                        _poolSize(newInfo)
                                    ) {
                                        interestDelta = (_poolSize(newInfo) - newInfo.borrows).divDecimal(
                                            Decimal.ONE + reserveFactor + insuranceFactor
                                        );
                                        uint256 interestPerSec = newInfo.borrows.mulDecimal(interestRate);
                                        if (interestPerSec > 0) {
                                            // Previous last accrual plus interest divided by interest speed (rounded up)
                                            newInfo.lastAccrual =
                                                _info.lastAccrual +
                                                (interestDelta + interestPerSec - 1) /
                                                interestPerSec;
                                        }
                                        reservesDelta = interestDelta.mulDecimal(reserveFactor);
                                        insuranceDelta = interestDelta.mulDecimal(insuranceFactor);
                                        newInfo.state = State.Default;
                                    }
                                    newInfo.borrows += interestDelta;
                                    newInfo.reserves += reservesDelta;
                                    newInfo.insurance += insuranceDelta;
                                    return newInfo;
                                }
                                // MODIFIERS
                                /// @notice Modifier to accrue interest and check that pool is currently active (possibly in warning)
                                modifier onlyActiveAccrual() {
                                    _accrueInterest();
                                    State currentState = _state(_info);
                                    require(
                                        currentState == State.Active ||
                                            currentState == State.Warning ||
                                            currentState == State.ProvisionalDefault,
                                        "PIA"
                                    );
                                    _;
                                }
                                /// @notice Modifier for functions restricted to manager
                                modifier onlyManager() {
                                    require(msg.sender == manager, "OM");
                                    _;
                                }
                                /// @notice Modifier for functions restricted to protocol governor
                                modifier onlyGovernor() {
                                    require(msg.sender == factory.owner(), "OG");
                                    _;
                                }
                                /// @notice Modifier for functions restricted to auction contract
                                modifier onlyAuction() {
                                    require(msg.sender == factory.auction(), "OA");
                                    _;
                                }
                                /// @notice Modifier for the functions restricted to factory
                                modifier onlyFactory() {
                                    require(msg.sender == address(factory), "OF");
                                    _;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            library Decimal {
                                /// @notice Number one as 18-digit decimal
                                uint256 internal constant ONE = 1e18;
                                /**
                                 * @notice Internal function for 10-digits decimal division
                                 * @param number Integer number
                                 * @param decimal Decimal number
                                 * @return Returns multiplied numbers
                                 */
                                function mulDecimal(uint256 number, uint256 decimal)
                                    internal
                                    pure
                                    returns (uint256)
                                {
                                    return (number * decimal) / ONE;
                                }
                                /**
                                 * @notice Internal function for 10-digits decimal multiplication
                                 * @param number Integer number
                                 * @param decimal Decimal number
                                 * @return Returns integer number divided by second
                                 */
                                function divDecimal(uint256 number, uint256 decimal)
                                    internal
                                    pure
                                    returns (uint256)
                                {
                                    return (number * ONE) / decimal;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Interface of the ERC20 standard as defined in the EIP.
                             */
                            interface IERC20Upgradeable {
                                /**
                                 * @dev Emitted when `value` tokens are moved from one account (`from`) to
                                 * another (`to`).
                                 *
                                 * Note that `value` may be zero.
                                 */
                                event Transfer(address indexed from, address indexed to, uint256 value);
                                /**
                                 * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                                 * a call to {approve}. `value` is the new allowance.
                                 */
                                event Approval(address indexed owner, address indexed spender, uint256 value);
                                /**
                                 * @dev Returns the amount of tokens in existence.
                                 */
                                function totalSupply() external view returns (uint256);
                                /**
                                 * @dev Returns the amount of tokens owned by `account`.
                                 */
                                function balanceOf(address account) external view returns (uint256);
                                /**
                                 * @dev Moves `amount` tokens from the caller's account to `to`.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * Emits a {Transfer} event.
                                 */
                                function transfer(address to, uint256 amount) external returns (bool);
                                /**
                                 * @dev Returns the remaining number of tokens that `spender` will be
                                 * allowed to spend on behalf of `owner` through {transferFrom}. This is
                                 * zero by default.
                                 *
                                 * This value changes when {approve} or {transferFrom} are called.
                                 */
                                function allowance(address owner, address spender) external view returns (uint256);
                                /**
                                 * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * IMPORTANT: Beware that changing an allowance with this method brings the risk
                                 * that someone may use both the old and the new allowance by unfortunate
                                 * transaction ordering. One possible solution to mitigate this race
                                 * condition is to first reduce the spender's allowance to 0 and set the
                                 * desired value afterwards:
                                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                                 *
                                 * Emits an {Approval} event.
                                 */
                                function approve(address spender, uint256 amount) external returns (bool);
                                /**
                                 * @dev Moves `amount` tokens from `from` to `to` using the
                                 * allowance mechanism. `amount` is then deducted from the caller's
                                 * allowance.
                                 *
                                 * Returns a boolean value indicating whether the operation succeeded.
                                 *
                                 * Emits a {Transfer} event.
                                 */
                                function transferFrom(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) external returns (bool);
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)
                            pragma solidity ^0.8.0;
                            import "../IERC20Upgradeable.sol";
                            import "../extensions/draft-IERC20PermitUpgradeable.sol";
                            import "../../../utils/AddressUpgradeable.sol";
                            /**
                             * @title SafeERC20
                             * @dev Wrappers around ERC20 operations that throw on failure (when the token
                             * contract returns false). Tokens that return no value (and instead revert or
                             * throw on failure) are also supported, non-reverting calls are assumed to be
                             * successful.
                             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                             */
                            library SafeERC20Upgradeable {
                                using AddressUpgradeable for address;
                                function safeTransfer(
                                    IERC20Upgradeable token,
                                    address to,
                                    uint256 value
                                ) internal {
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                                }
                                function safeTransferFrom(
                                    IERC20Upgradeable token,
                                    address from,
                                    address to,
                                    uint256 value
                                ) internal {
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                                }
                                /**
                                 * @dev Deprecated. This function has issues similar to the ones found in
                                 * {IERC20-approve}, and its usage is discouraged.
                                 *
                                 * Whenever possible, use {safeIncreaseAllowance} and
                                 * {safeDecreaseAllowance} instead.
                                 */
                                function safeApprove(
                                    IERC20Upgradeable token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    // safeApprove should only be called when setting an initial allowance,
                                    // or when resetting it to zero. To increase and decrease it, use
                                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                                    require(
                                        (value == 0) || (token.allowance(address(this), spender) == 0),
                                        "SafeERC20: approve from non-zero to non-zero allowance"
                                    );
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                                }
                                function safeIncreaseAllowance(
                                    IERC20Upgradeable token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    uint256 newAllowance = token.allowance(address(this), spender) + value;
                                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                }
                                function safeDecreaseAllowance(
                                    IERC20Upgradeable token,
                                    address spender,
                                    uint256 value
                                ) internal {
                                    unchecked {
                                        uint256 oldAllowance = token.allowance(address(this), spender);
                                        require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                                        uint256 newAllowance = oldAllowance - value;
                                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                    }
                                }
                                function safePermit(
                                    IERC20PermitUpgradeable token,
                                    address owner,
                                    address spender,
                                    uint256 value,
                                    uint256 deadline,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) internal {
                                    uint256 nonceBefore = token.nonces(owner);
                                    token.permit(owner, spender, value, deadline, v, r, s);
                                    uint256 nonceAfter = token.nonces(owner);
                                    require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                                }
                                /**
                                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                                 * @param token The token targeted by the call.
                                 * @param data The call data (encoded using abi.encode or one of its variants).
                                 */
                                function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
                                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                                    // the target address contains contract code and also asserts for success in the low-level call.
                                    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                                    if (returndata.length > 0) {
                                        // Return data is optional
                                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                             * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                             *
                             * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                             * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                             * need to send a transaction, and thus is not required to hold Ether at all.
                             */
                            interface IERC20PermitUpgradeable {
                                /**
                                 * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                                 * given ``owner``'s signed approval.
                                 *
                                 * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                                 * ordering also apply here.
                                 *
                                 * Emits an {Approval} event.
                                 *
                                 * Requirements:
                                 *
                                 * - `spender` cannot be the zero address.
                                 * - `deadline` must be a timestamp in the future.
                                 * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                                 * over the EIP712-formatted function arguments.
                                 * - the signature must use ``owner``'s current nonce (see {nonces}).
                                 *
                                 * For more information on the signature format, see the
                                 * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                                 * section].
                                 */
                                function permit(
                                    address owner,
                                    address spender,
                                    uint256 value,
                                    uint256 deadline,
                                    uint8 v,
                                    bytes32 r,
                                    bytes32 s
                                ) external;
                                /**
                                 * @dev Returns the current nonce for `owner`. This value must be
                                 * included whenever a signature is generated for {permit}.
                                 *
                                 * Every successful call to {permit} increases ``owner``'s nonce by one. This
                                 * prevents a signature from being used multiple times.
                                 */
                                function nonces(address owner) external view returns (uint256);
                                /**
                                 * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                                 */
                                // solhint-disable-next-line func-name-mixedcase
                                function DOMAIN_SEPARATOR() external view returns (bytes32);
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
                            import "../interfaces/IPoolFactory.sol";
                            import "../interfaces/IInterestRateModel.sol";
                            /// @notice This contract describes Pool's storage, events and initializer
                            abstract contract PoolBaseInfo is ERC20Upgradeable {
                                /// @notice Address of the pool's manager
                                address public manager;
                                /// @notice Pool currency token
                                IERC20Upgradeable public currency;
                                /// @notice PoolFactory contract
                                IPoolFactory public factory;
                                /// @notice InterestRateModel contract address
                                IInterestRateModel public interestRateModel;
                                /// @notice Reserve factor as 18-digit decimal
                                uint256 public reserveFactor;
                                /// @notice Insurance factor as 18-digit decimal
                                uint256 public insuranceFactor;
                                /// @notice Pool utilization that leads to warning state (as 18-digit decimal)
                                uint256 public warningUtilization;
                                /// @notice Pool utilization that leads to provisional default (as 18-digit decimal)
                                uint256 public provisionalDefaultUtilization;
                                /// @notice Grace period for warning state before pool goes to default (in seconds)
                                uint256 public warningGracePeriod;
                                /// @notice Max period for which pool can stay not active before it can be closed by governor (in seconds)
                                uint256 public maxInactivePeriod;
                                /// @notice Period after default to start auction after which pool can be closed by anyone (in seconds)
                                uint256 public periodToStartAuction;
                                enum State {
                                    Active,
                                    Warning,
                                    ProvisionalDefault,
                                    Default,
                                    Closed
                                }
                                /// @notice Indicator if debt has been claimed
                                bool public debtClaimed;
                                /// @notice Structure describing all pool's borrows details
                                struct BorrowInfo {
                                    uint256 principal;
                                    uint256 borrows;
                                    uint256 reserves;
                                    uint256 insurance;
                                    uint256 lastAccrual;
                                    uint256 enteredProvisionalDefault;
                                    uint256 enteredZeroUtilization;
                                    State state;
                                }
                                /// @notice Last updated borrow info
                                BorrowInfo internal _info;
                                /// @notice Pool's symbol
                                string internal _symbol;
                                // EVENTS
                                /// @notice Event emitted when pool is closed
                                event Closed();
                                /// @notice Event emitted when liquidity is provided to the Pool
                                /// @param provider Address who provided liquidity
                                /// @param referral Optional referral address
                                /// @param currencyAmount Amount of pool's currency provided
                                /// @param tokens Amount of r-tokens received by provider in response
                                event Provided(
                                    address indexed provider,
                                    address indexed referral,
                                    uint256 currencyAmount,
                                    uint256 tokens
                                );
                                /// @notice Event emitted when liquidity is redeemed from the Pool
                                /// @param redeemer Address who redeems liquidity
                                /// @param currencyAmount Amount of currency received by redeemer
                                /// @param tokens Amount of given and burned r-tokens
                                event Redeemed(
                                    address indexed redeemer,
                                    uint256 currencyAmount,
                                    uint256 tokens
                                );
                                /// @notice Event emitted when manager assignes liquidity
                                /// @param amount Amount of currency borrower
                                /// @param receiver Address where borrow has been transferred
                                event Borrowed(uint256 amount, address indexed receiver);
                                /// @notice Event emitted when manager returns liquidity assignment
                                /// @param amount Amount of currency repaid
                                event Repaid(uint256 amount);
                                // CONSTRUCTOR
                                /// @notice Upgradeable contract constructor
                                /// @param manager_ Address of the Pool's manager
                                /// @param currency_ Address of the currency token
                                function __PoolBaseInfo_init(address manager_, IERC20Upgradeable currency_)
                                    internal
                                    onlyInitializing
                                {
                                    require(manager_ != address(0), "AIZ");
                                    require(address(currency_) != address(0), "AIZ");
                                    manager = manager_;
                                    currency = currency_;
                                    factory = IPoolFactory(msg.sender);
                                    interestRateModel = IInterestRateModel(factory.interestRateModel());
                                    reserveFactor = factory.reserveFactor();
                                    insuranceFactor = factory.insuranceFactor();
                                    warningUtilization = factory.warningUtilization();
                                    provisionalDefaultUtilization = factory.provisionalDefaultUtilization();
                                    warningGracePeriod = factory.warningGracePeriod();
                                    maxInactivePeriod = factory.maxInactivePeriod();
                                    periodToStartAuction = factory.periodToStartAuction();
                                    _symbol = factory.getPoolSymbol(address(currency), address(manager));
                                    __ERC20_init(string(bytes.concat(bytes("Pool "), bytes(_symbol))), "");
                                    _info.enteredZeroUtilization = block.timestamp;
                                    _info.lastAccrual = block.timestamp;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            interface IAuction {
                                function bid(address pool, uint256 amount) external;
                                function ownerOfDebt(address pool) external view returns (address);
                                /// @notice States of auction
                                /// @dev None: A pool is not default and auction can't be started
                                /// @dev NotStarted: A pool is default and auction can be started
                                /// @dev Active: An auction is started
                                /// @dev Finished: An auction is finished but NFT is not claimed
                                /// @dev Closed: An auction is finished and NFT is claimed
                                enum State {
                                    None,
                                    NotStarted,
                                    Active,
                                    Finished,
                                    Closed
                                }
                                function state(address pool) external view returns (State);
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
                            pragma solidity ^0.8.1;
                            /**
                             * @dev Collection of functions related to the address type
                             */
                            library AddressUpgradeable {
                                /**
                                 * @dev Returns true if `account` is a contract.
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * It is unsafe to assume that an address for which this function returns
                                 * false is an externally-owned account (EOA) and not a contract.
                                 *
                                 * Among others, `isContract` will return false for the following
                                 * types of addresses:
                                 *
                                 *  - an externally-owned account
                                 *  - a contract in construction
                                 *  - an address where a contract will be created
                                 *  - an address where a contract lived, but was destroyed
                                 * ====
                                 *
                                 * [IMPORTANT]
                                 * ====
                                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                                 *
                                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                                 * constructor.
                                 * ====
                                 */
                                function isContract(address account) internal view returns (bool) {
                                    // This method relies on extcodesize/address.code.length, which returns 0
                                    // for contracts in construction, since the code is only stored at the end
                                    // of the constructor execution.
                                    return account.code.length > 0;
                                }
                                /**
                                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                                 * `recipient`, forwarding all available gas and reverting on errors.
                                 *
                                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                                 * imposed by `transfer`, making them unable to receive funds via
                                 * `transfer`. {sendValue} removes this limitation.
                                 *
                                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                                 *
                                 * IMPORTANT: because control is transferred to `recipient`, care must be
                                 * taken to not create reentrancy vulnerabilities. Consider using
                                 * {ReentrancyGuard} or the
                                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                                 */
                                function sendValue(address payable recipient, uint256 amount) internal {
                                    require(address(this).balance >= amount, "Address: insufficient balance");
                                    (bool success, ) = recipient.call{value: amount}("");
                                    require(success, "Address: unable to send value, recipient may have reverted");
                                }
                                /**
                                 * @dev Performs a Solidity function call using a low level `call`. A
                                 * plain `call` is an unsafe replacement for a function call: use this
                                 * function instead.
                                 *
                                 * If `target` reverts with a revert reason, it is bubbled up by this
                                 * function (like regular Solidity function calls).
                                 *
                                 * Returns the raw returned data. To convert to the expected return value,
                                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                                 *
                                 * Requirements:
                                 *
                                 * - `target` must be a contract.
                                 * - calling `target` with `data` must not revert.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                    return functionCall(target, data, "Address: low-level call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                                 * `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, 0, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but also transferring `value` wei to `target`.
                                 *
                                 * Requirements:
                                 *
                                 * - the calling contract must have an ETH balance of at least `value`.
                                 * - the called Solidity function must be `payable`.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value
                                ) internal returns (bytes memory) {
                                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                                 *
                                 * _Available since v3.1._
                                 */
                                function functionCallWithValue(
                                    address target,
                                    bytes memory data,
                                    uint256 value,
                                    string memory errorMessage
                                ) internal returns (bytes memory) {
                                    require(address(this).balance >= value, "Address: insufficient balance for call");
                                    require(isContract(target), "Address: call to non-contract");
                                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                    return functionStaticCall(target, data, "Address: low-level static call failed");
                                }
                                /**
                                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                                 * but performing a static call.
                                 *
                                 * _Available since v3.3._
                                 */
                                function functionStaticCall(
                                    address target,
                                    bytes memory data,
                                    string memory errorMessage
                                ) internal view returns (bytes memory) {
                                    require(isContract(target), "Address: static call to non-contract");
                                    (bool success, bytes memory returndata) = target.staticcall(data);
                                    return verifyCallResult(success, returndata, errorMessage);
                                }
                                /**
                                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                                 * revert reason using the provided one.
                                 *
                                 * _Available since v4.3._
                                 */
                                function verifyCallResult(
                                    bool success,
                                    bytes memory returndata,
                                    string memory errorMessage
                                ) internal pure returns (bytes memory) {
                                    if (success) {
                                        return returndata;
                                    } else {
                                        // Look for revert reason and bubble it up if present
                                        if (returndata.length > 0) {
                                            // The easiest way to bubble the revert reason is using memory via assembly
                                            /// @solidity memory-safe-assembly
                                            assembly {
                                                let returndata_size := mload(returndata)
                                                revert(add(32, returndata), returndata_size)
                                            }
                                        } else {
                                            revert(errorMessage);
                                        }
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
                            pragma solidity ^0.8.0;
                            import "./IERC20Upgradeable.sol";
                            import "./extensions/IERC20MetadataUpgradeable.sol";
                            import "../../utils/ContextUpgradeable.sol";
                            import "../../proxy/utils/Initializable.sol";
                            /**
                             * @dev Implementation of the {IERC20} interface.
                             *
                             * This implementation is agnostic to the way tokens are created. This means
                             * that a supply mechanism has to be added in a derived contract using {_mint}.
                             * For a generic mechanism see {ERC20PresetMinterPauser}.
                             *
                             * TIP: For a detailed writeup see our guide
                             * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                             * to implement supply mechanisms].
                             *
                             * We have followed general OpenZeppelin Contracts guidelines: functions revert
                             * instead returning `false` on failure. This behavior is nonetheless
                             * conventional and does not conflict with the expectations of ERC20
                             * applications.
                             *
                             * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                             * This allows applications to reconstruct the allowance for all accounts just
                             * by listening to said events. Other implementations of the EIP may not emit
                             * these events, as it isn't required by the specification.
                             *
                             * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                             * functions have been added to mitigate the well-known issues around setting
                             * allowances. See {IERC20-approve}.
                             */
                            contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
                                mapping(address => uint256) private _balances;
                                mapping(address => mapping(address => uint256)) private _allowances;
                                uint256 private _totalSupply;
                                string private _name;
                                string private _symbol;
                                /**
                                 * @dev Sets the values for {name} and {symbol}.
                                 *
                                 * The default value of {decimals} is 18. To select a different value for
                                 * {decimals} you should overload it.
                                 *
                                 * All two of these values are immutable: they can only be set once during
                                 * construction.
                                 */
                                function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
                                    __ERC20_init_unchained(name_, symbol_);
                                }
                                function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                                    _name = name_;
                                    _symbol = symbol_;
                                }
                                /**
                                 * @dev Returns the name of the token.
                                 */
                                function name() public view virtual override returns (string memory) {
                                    return _name;
                                }
                                /**
                                 * @dev Returns the symbol of the token, usually a shorter version of the
                                 * name.
                                 */
                                function symbol() public view virtual override returns (string memory) {
                                    return _symbol;
                                }
                                /**
                                 * @dev Returns the number of decimals used to get its user representation.
                                 * For example, if `decimals` equals `2`, a balance of `505` tokens should
                                 * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                                 *
                                 * Tokens usually opt for a value of 18, imitating the relationship between
                                 * Ether and Wei. This is the value {ERC20} uses, unless this function is
                                 * overridden;
                                 *
                                 * NOTE: This information is only used for _display_ purposes: it in
                                 * no way affects any of the arithmetic of the contract, including
                                 * {IERC20-balanceOf} and {IERC20-transfer}.
                                 */
                                function decimals() public view virtual override returns (uint8) {
                                    return 18;
                                }
                                /**
                                 * @dev See {IERC20-totalSupply}.
                                 */
                                function totalSupply() public view virtual override returns (uint256) {
                                    return _totalSupply;
                                }
                                /**
                                 * @dev See {IERC20-balanceOf}.
                                 */
                                function balanceOf(address account) public view virtual override returns (uint256) {
                                    return _balances[account];
                                }
                                /**
                                 * @dev See {IERC20-transfer}.
                                 *
                                 * Requirements:
                                 *
                                 * - `to` cannot be the zero address.
                                 * - the caller must have a balance of at least `amount`.
                                 */
                                function transfer(address to, uint256 amount) public virtual override returns (bool) {
                                    address owner = _msgSender();
                                    _transfer(owner, to, amount);
                                    return true;
                                }
                                /**
                                 * @dev See {IERC20-allowance}.
                                 */
                                function allowance(address owner, address spender) public view virtual override returns (uint256) {
                                    return _allowances[owner][spender];
                                }
                                /**
                                 * @dev See {IERC20-approve}.
                                 *
                                 * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                                 * `transferFrom`. This is semantically equivalent to an infinite approval.
                                 *
                                 * Requirements:
                                 *
                                 * - `spender` cannot be the zero address.
                                 */
                                function approve(address spender, uint256 amount) public virtual override returns (bool) {
                                    address owner = _msgSender();
                                    _approve(owner, spender, amount);
                                    return true;
                                }
                                /**
                                 * @dev See {IERC20-transferFrom}.
                                 *
                                 * Emits an {Approval} event indicating the updated allowance. This is not
                                 * required by the EIP. See the note at the beginning of {ERC20}.
                                 *
                                 * NOTE: Does not update the allowance if the current allowance
                                 * is the maximum `uint256`.
                                 *
                                 * Requirements:
                                 *
                                 * - `from` and `to` cannot be the zero address.
                                 * - `from` must have a balance of at least `amount`.
                                 * - the caller must have allowance for ``from``'s tokens of at least
                                 * `amount`.
                                 */
                                function transferFrom(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) public virtual override returns (bool) {
                                    address spender = _msgSender();
                                    _spendAllowance(from, spender, amount);
                                    _transfer(from, to, amount);
                                    return true;
                                }
                                /**
                                 * @dev Atomically increases the allowance granted to `spender` by the caller.
                                 *
                                 * This is an alternative to {approve} that can be used as a mitigation for
                                 * problems described in {IERC20-approve}.
                                 *
                                 * Emits an {Approval} event indicating the updated allowance.
                                 *
                                 * Requirements:
                                 *
                                 * - `spender` cannot be the zero address.
                                 */
                                function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                                    address owner = _msgSender();
                                    _approve(owner, spender, allowance(owner, spender) + addedValue);
                                    return true;
                                }
                                /**
                                 * @dev Atomically decreases the allowance granted to `spender` by the caller.
                                 *
                                 * This is an alternative to {approve} that can be used as a mitigation for
                                 * problems described in {IERC20-approve}.
                                 *
                                 * Emits an {Approval} event indicating the updated allowance.
                                 *
                                 * Requirements:
                                 *
                                 * - `spender` cannot be the zero address.
                                 * - `spender` must have allowance for the caller of at least
                                 * `subtractedValue`.
                                 */
                                function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                                    address owner = _msgSender();
                                    uint256 currentAllowance = allowance(owner, spender);
                                    require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                                    unchecked {
                                        _approve(owner, spender, currentAllowance - subtractedValue);
                                    }
                                    return true;
                                }
                                /**
                                 * @dev Moves `amount` of tokens from `from` to `to`.
                                 *
                                 * This internal function is equivalent to {transfer}, and can be used to
                                 * e.g. implement automatic token fees, slashing mechanisms, etc.
                                 *
                                 * Emits a {Transfer} event.
                                 *
                                 * Requirements:
                                 *
                                 * - `from` cannot be the zero address.
                                 * - `to` cannot be the zero address.
                                 * - `from` must have a balance of at least `amount`.
                                 */
                                function _transfer(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) internal virtual {
                                    require(from != address(0), "ERC20: transfer from the zero address");
                                    require(to != address(0), "ERC20: transfer to the zero address");
                                    _beforeTokenTransfer(from, to, amount);
                                    uint256 fromBalance = _balances[from];
                                    require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                                    unchecked {
                                        _balances[from] = fromBalance - amount;
                                    }
                                    _balances[to] += amount;
                                    emit Transfer(from, to, amount);
                                    _afterTokenTransfer(from, to, amount);
                                }
                                /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                                 * the total supply.
                                 *
                                 * Emits a {Transfer} event with `from` set to the zero address.
                                 *
                                 * Requirements:
                                 *
                                 * - `account` cannot be the zero address.
                                 */
                                function _mint(address account, uint256 amount) internal virtual {
                                    require(account != address(0), "ERC20: mint to the zero address");
                                    _beforeTokenTransfer(address(0), account, amount);
                                    _totalSupply += amount;
                                    _balances[account] += amount;
                                    emit Transfer(address(0), account, amount);
                                    _afterTokenTransfer(address(0), account, amount);
                                }
                                /**
                                 * @dev Destroys `amount` tokens from `account`, reducing the
                                 * total supply.
                                 *
                                 * Emits a {Transfer} event with `to` set to the zero address.
                                 *
                                 * Requirements:
                                 *
                                 * - `account` cannot be the zero address.
                                 * - `account` must have at least `amount` tokens.
                                 */
                                function _burn(address account, uint256 amount) internal virtual {
                                    require(account != address(0), "ERC20: burn from the zero address");
                                    _beforeTokenTransfer(account, address(0), amount);
                                    uint256 accountBalance = _balances[account];
                                    require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                                    unchecked {
                                        _balances[account] = accountBalance - amount;
                                    }
                                    _totalSupply -= amount;
                                    emit Transfer(account, address(0), amount);
                                    _afterTokenTransfer(account, address(0), amount);
                                }
                                /**
                                 * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                                 *
                                 * This internal function is equivalent to `approve`, and can be used to
                                 * e.g. set automatic allowances for certain subsystems, etc.
                                 *
                                 * Emits an {Approval} event.
                                 *
                                 * Requirements:
                                 *
                                 * - `owner` cannot be the zero address.
                                 * - `spender` cannot be the zero address.
                                 */
                                function _approve(
                                    address owner,
                                    address spender,
                                    uint256 amount
                                ) internal virtual {
                                    require(owner != address(0), "ERC20: approve from the zero address");
                                    require(spender != address(0), "ERC20: approve to the zero address");
                                    _allowances[owner][spender] = amount;
                                    emit Approval(owner, spender, amount);
                                }
                                /**
                                 * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                                 *
                                 * Does not update the allowance amount in case of infinite allowance.
                                 * Revert if not enough allowance is available.
                                 *
                                 * Might emit an {Approval} event.
                                 */
                                function _spendAllowance(
                                    address owner,
                                    address spender,
                                    uint256 amount
                                ) internal virtual {
                                    uint256 currentAllowance = allowance(owner, spender);
                                    if (currentAllowance != type(uint256).max) {
                                        require(currentAllowance >= amount, "ERC20: insufficient allowance");
                                        unchecked {
                                            _approve(owner, spender, currentAllowance - amount);
                                        }
                                    }
                                }
                                /**
                                 * @dev Hook that is called before any transfer of tokens. This includes
                                 * minting and burning.
                                 *
                                 * Calling conditions:
                                 *
                                 * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                                 * will be transferred to `to`.
                                 * - when `from` is zero, `amount` tokens will be minted for `to`.
                                 * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                                 * - `from` and `to` are never both zero.
                                 *
                                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                                 */
                                function _beforeTokenTransfer(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) internal virtual {}
                                /**
                                 * @dev Hook that is called after any transfer of tokens. This includes
                                 * minting and burning.
                                 *
                                 * Calling conditions:
                                 *
                                 * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                                 * has been transferred to `to`.
                                 * - when `from` is zero, `amount` tokens have been minted for `to`.
                                 * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                                 * - `from` and `to` are never both zero.
                                 *
                                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                                 */
                                function _afterTokenTransfer(
                                    address from,
                                    address to,
                                    uint256 amount
                                ) internal virtual {}
                                /**
                                 * @dev This empty reserved space is put in place to allow future versions to add new
                                 * variables without shifting down storage in the inheritance chain.
                                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                                 */
                                uint256[45] private __gap;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            interface IPoolFactory {
                                function getPoolSymbol(address currency, address manager)
                                    external
                                    view
                                    returns (string memory);
                                function isPool(address pool) external view returns (bool);
                                function interestRateModel() external view returns (address);
                                function auction() external view returns (address);
                                function treasury() external view returns (address);
                                function reserveFactor() external view returns (uint256);
                                function insuranceFactor() external view returns (uint256);
                                function warningUtilization() external view returns (uint256);
                                function provisionalDefaultUtilization() external view returns (uint256);
                                function warningGracePeriod() external view returns (uint256);
                                function maxInactivePeriod() external view returns (uint256);
                                function periodToStartAuction() external view returns (uint256);
                                function owner() external view returns (address);
                                function closePool() external;
                                function burnStake() external;
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            interface IInterestRateModel {
                                function getBorrowRate(
                                    uint256 balance,
                                    uint256 totalBorrows,
                                    uint256 totalReserves
                                ) external view returns (uint256);
                                function utilizationRate(
                                    uint256 balance,
                                    uint256 borrows,
                                    uint256 reserves
                                ) external pure returns (uint256);
                                function getSupplyRate(
                                    uint256 balance,
                                    uint256 borrows,
                                    uint256 reserves,
                                    uint256 reserveFactor
                                ) external view returns (uint256);
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                            pragma solidity ^0.8.0;
                            import "../proxy/utils/Initializable.sol";
                            /**
                             * @dev Provides information about the current execution context, including the
                             * sender of the transaction and its data. While these are generally available
                             * via msg.sender and msg.data, they should not be accessed in such a direct
                             * manner, since when dealing with meta-transactions the account sending and
                             * paying for execution may not be the actual sender (as far as an application
                             * is concerned).
                             *
                             * This contract is only required for intermediate, library-like contracts.
                             */
                            abstract contract ContextUpgradeable is Initializable {
                                function __Context_init() internal onlyInitializing {
                                }
                                function __Context_init_unchained() internal onlyInitializing {
                                }
                                function _msgSender() internal view virtual returns (address) {
                                    return msg.sender;
                                }
                                function _msgData() internal view virtual returns (bytes calldata) {
                                    return msg.data;
                                }
                                /**
                                 * @dev This empty reserved space is put in place to allow future versions to add new
                                 * variables without shifting down storage in the inheritance chain.
                                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                                 */
                                uint256[50] private __gap;
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
                            pragma solidity ^0.8.2;
                            import "../../utils/AddressUpgradeable.sol";
                            /**
                             * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                             * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
                             * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                             * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                             *
                             * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
                             * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
                             * case an upgrade adds a module that needs to be initialized.
                             *
                             * For example:
                             *
                             * [.hljs-theme-light.nopadding]
                             * ```
                             * contract MyToken is ERC20Upgradeable {
                             *     function initialize() initializer public {
                             *         __ERC20_init("MyToken", "MTK");
                             *     }
                             * }
                             * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
                             *     function initializeV2() reinitializer(2) public {
                             *         __ERC20Permit_init("MyToken");
                             *     }
                             * }
                             * ```
                             *
                             * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                             * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                             *
                             * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                             * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                             *
                             * [CAUTION]
                             * ====
                             * Avoid leaving a contract uninitialized.
                             *
                             * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
                             * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
                             * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
                             *
                             * [.hljs-theme-light.nopadding]
                             * ```
                             * /// @custom:oz-upgrades-unsafe-allow constructor
                             * constructor() {
                             *     _disableInitializers();
                             * }
                             * ```
                             * ====
                             */
                            abstract contract Initializable {
                                /**
                                 * @dev Indicates that the contract has been initialized.
                                 * @custom:oz-retyped-from bool
                                 */
                                uint8 private _initialized;
                                /**
                                 * @dev Indicates that the contract is in the process of being initialized.
                                 */
                                bool private _initializing;
                                /**
                                 * @dev Triggered when the contract has been initialized or reinitialized.
                                 */
                                event Initialized(uint8 version);
                                /**
                                 * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                                 * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
                                 */
                                modifier initializer() {
                                    bool isTopLevelCall = !_initializing;
                                    require(
                                        (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                                        "Initializable: contract is already initialized"
                                    );
                                    _initialized = 1;
                                    if (isTopLevelCall) {
                                        _initializing = true;
                                    }
                                    _;
                                    if (isTopLevelCall) {
                                        _initializing = false;
                                        emit Initialized(1);
                                    }
                                }
                                /**
                                 * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                                 * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                                 * used to initialize parent contracts.
                                 *
                                 * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
                                 * initialization step. This is essential to configure modules that are added through upgrades and that require
                                 * initialization.
                                 *
                                 * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                                 * a contract, executing them in the right order is up to the developer or operator.
                                 */
                                modifier reinitializer(uint8 version) {
                                    require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                                    _initialized = version;
                                    _initializing = true;
                                    _;
                                    _initializing = false;
                                    emit Initialized(version);
                                }
                                /**
                                 * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                                 * {initializer} and {reinitializer} modifiers, directly or indirectly.
                                 */
                                modifier onlyInitializing() {
                                    require(_initializing, "Initializable: contract is not initializing");
                                    _;
                                }
                                /**
                                 * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                                 * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                                 * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                                 * through proxies.
                                 */
                                function _disableInitializers() internal virtual {
                                    require(!_initializing, "Initializable: contract is initializing");
                                    if (_initialized < type(uint8).max) {
                                        _initialized = type(uint8).max;
                                        emit Initialized(type(uint8).max);
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                             * checks.
                             *
                             * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                             * easily result in undesired exploitation or bugs, since developers usually
                             * assume that overflows raise errors. `SafeCast` restores this intuition by
                             * reverting the transaction when such an operation overflows.
                             *
                             * Using this library instead of the unchecked operations eliminates an entire
                             * class of bugs, so it's recommended to use it always.
                             *
                             * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                             * all math on `uint256` and `int256` and then downcasting.
                             */
                            library SafeCastUpgradeable {
                                /**
                                 * @dev Returns the downcasted uint248 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint248).
                                 *
                                 * Counterpart to Solidity's `uint248` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 248 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint248(uint256 value) internal pure returns (uint248) {
                                    require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
                                    return uint248(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint240 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint240).
                                 *
                                 * Counterpart to Solidity's `uint240` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 240 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint240(uint256 value) internal pure returns (uint240) {
                                    require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
                                    return uint240(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint232 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint232).
                                 *
                                 * Counterpart to Solidity's `uint232` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 232 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint232(uint256 value) internal pure returns (uint232) {
                                    require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
                                    return uint232(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint224 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint224).
                                 *
                                 * Counterpart to Solidity's `uint224` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 224 bits
                                 *
                                 * _Available since v4.2._
                                 */
                                function toUint224(uint256 value) internal pure returns (uint224) {
                                    require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                                    return uint224(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint216 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint216).
                                 *
                                 * Counterpart to Solidity's `uint216` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 216 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint216(uint256 value) internal pure returns (uint216) {
                                    require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
                                    return uint216(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint208 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint208).
                                 *
                                 * Counterpart to Solidity's `uint208` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 208 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint208(uint256 value) internal pure returns (uint208) {
                                    require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
                                    return uint208(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint200 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint200).
                                 *
                                 * Counterpart to Solidity's `uint200` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 200 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint200(uint256 value) internal pure returns (uint200) {
                                    require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
                                    return uint200(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint192 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint192).
                                 *
                                 * Counterpart to Solidity's `uint192` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 192 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint192(uint256 value) internal pure returns (uint192) {
                                    require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
                                    return uint192(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint184 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint184).
                                 *
                                 * Counterpart to Solidity's `uint184` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 184 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint184(uint256 value) internal pure returns (uint184) {
                                    require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
                                    return uint184(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint176 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint176).
                                 *
                                 * Counterpart to Solidity's `uint176` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 176 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint176(uint256 value) internal pure returns (uint176) {
                                    require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
                                    return uint176(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint168 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint168).
                                 *
                                 * Counterpart to Solidity's `uint168` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 168 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint168(uint256 value) internal pure returns (uint168) {
                                    require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
                                    return uint168(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint160 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint160).
                                 *
                                 * Counterpart to Solidity's `uint160` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 160 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint160(uint256 value) internal pure returns (uint160) {
                                    require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
                                    return uint160(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint152 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint152).
                                 *
                                 * Counterpart to Solidity's `uint152` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 152 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint152(uint256 value) internal pure returns (uint152) {
                                    require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
                                    return uint152(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint144 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint144).
                                 *
                                 * Counterpart to Solidity's `uint144` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 144 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint144(uint256 value) internal pure returns (uint144) {
                                    require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
                                    return uint144(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint136 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint136).
                                 *
                                 * Counterpart to Solidity's `uint136` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 136 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint136(uint256 value) internal pure returns (uint136) {
                                    require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
                                    return uint136(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint128 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint128).
                                 *
                                 * Counterpart to Solidity's `uint128` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 128 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint128(uint256 value) internal pure returns (uint128) {
                                    require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                                    return uint128(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint120 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint120).
                                 *
                                 * Counterpart to Solidity's `uint120` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 120 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint120(uint256 value) internal pure returns (uint120) {
                                    require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
                                    return uint120(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint112 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint112).
                                 *
                                 * Counterpart to Solidity's `uint112` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 112 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint112(uint256 value) internal pure returns (uint112) {
                                    require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
                                    return uint112(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint104 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint104).
                                 *
                                 * Counterpart to Solidity's `uint104` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 104 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint104(uint256 value) internal pure returns (uint104) {
                                    require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
                                    return uint104(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint96 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint96).
                                 *
                                 * Counterpart to Solidity's `uint96` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 96 bits
                                 *
                                 * _Available since v4.2._
                                 */
                                function toUint96(uint256 value) internal pure returns (uint96) {
                                    require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                                    return uint96(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint88 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint88).
                                 *
                                 * Counterpart to Solidity's `uint88` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 88 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint88(uint256 value) internal pure returns (uint88) {
                                    require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
                                    return uint88(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint80 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint80).
                                 *
                                 * Counterpart to Solidity's `uint80` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 80 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint80(uint256 value) internal pure returns (uint80) {
                                    require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
                                    return uint80(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint72 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint72).
                                 *
                                 * Counterpart to Solidity's `uint72` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 72 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint72(uint256 value) internal pure returns (uint72) {
                                    require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
                                    return uint72(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint64 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint64).
                                 *
                                 * Counterpart to Solidity's `uint64` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 64 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint64(uint256 value) internal pure returns (uint64) {
                                    require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                                    return uint64(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint56 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint56).
                                 *
                                 * Counterpart to Solidity's `uint56` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 56 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint56(uint256 value) internal pure returns (uint56) {
                                    require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
                                    return uint56(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint48 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint48).
                                 *
                                 * Counterpart to Solidity's `uint48` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 48 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint48(uint256 value) internal pure returns (uint48) {
                                    require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
                                    return uint48(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint40 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint40).
                                 *
                                 * Counterpart to Solidity's `uint40` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 40 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint40(uint256 value) internal pure returns (uint40) {
                                    require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
                                    return uint40(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint32 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint32).
                                 *
                                 * Counterpart to Solidity's `uint32` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 32 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint32(uint256 value) internal pure returns (uint32) {
                                    require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                                    return uint32(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint24 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint24).
                                 *
                                 * Counterpart to Solidity's `uint24` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 24 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint24(uint256 value) internal pure returns (uint24) {
                                    require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
                                    return uint24(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint16 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint16).
                                 *
                                 * Counterpart to Solidity's `uint16` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 16 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint16(uint256 value) internal pure returns (uint16) {
                                    require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                                    return uint16(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint8 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint8).
                                 *
                                 * Counterpart to Solidity's `uint8` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 8 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint8(uint256 value) internal pure returns (uint8) {
                                    require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                                    return uint8(value);
                                }
                                /**
                                 * @dev Converts a signed int256 into an unsigned uint256.
                                 *
                                 * Requirements:
                                 *
                                 * - input must be greater than or equal to 0.
                                 *
                                 * _Available since v3.0._
                                 */
                                function toUint256(int256 value) internal pure returns (uint256) {
                                    require(value >= 0, "SafeCast: value must be positive");
                                    return uint256(value);
                                }
                                /**
                                 * @dev Returns the downcasted int248 from int256, reverting on
                                 * overflow (when the input is less than smallest int248 or
                                 * greater than largest int248).
                                 *
                                 * Counterpart to Solidity's `int248` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 248 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt248(int256 value) internal pure returns (int248) {
                                    require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits");
                                    return int248(value);
                                }
                                /**
                                 * @dev Returns the downcasted int240 from int256, reverting on
                                 * overflow (when the input is less than smallest int240 or
                                 * greater than largest int240).
                                 *
                                 * Counterpart to Solidity's `int240` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 240 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt240(int256 value) internal pure returns (int240) {
                                    require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits");
                                    return int240(value);
                                }
                                /**
                                 * @dev Returns the downcasted int232 from int256, reverting on
                                 * overflow (when the input is less than smallest int232 or
                                 * greater than largest int232).
                                 *
                                 * Counterpart to Solidity's `int232` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 232 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt232(int256 value) internal pure returns (int232) {
                                    require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits");
                                    return int232(value);
                                }
                                /**
                                 * @dev Returns the downcasted int224 from int256, reverting on
                                 * overflow (when the input is less than smallest int224 or
                                 * greater than largest int224).
                                 *
                                 * Counterpart to Solidity's `int224` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 224 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt224(int256 value) internal pure returns (int224) {
                                    require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits");
                                    return int224(value);
                                }
                                /**
                                 * @dev Returns the downcasted int216 from int256, reverting on
                                 * overflow (when the input is less than smallest int216 or
                                 * greater than largest int216).
                                 *
                                 * Counterpart to Solidity's `int216` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 216 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt216(int256 value) internal pure returns (int216) {
                                    require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits");
                                    return int216(value);
                                }
                                /**
                                 * @dev Returns the downcasted int208 from int256, reverting on
                                 * overflow (when the input is less than smallest int208 or
                                 * greater than largest int208).
                                 *
                                 * Counterpart to Solidity's `int208` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 208 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt208(int256 value) internal pure returns (int208) {
                                    require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits");
                                    return int208(value);
                                }
                                /**
                                 * @dev Returns the downcasted int200 from int256, reverting on
                                 * overflow (when the input is less than smallest int200 or
                                 * greater than largest int200).
                                 *
                                 * Counterpart to Solidity's `int200` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 200 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt200(int256 value) internal pure returns (int200) {
                                    require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits");
                                    return int200(value);
                                }
                                /**
                                 * @dev Returns the downcasted int192 from int256, reverting on
                                 * overflow (when the input is less than smallest int192 or
                                 * greater than largest int192).
                                 *
                                 * Counterpart to Solidity's `int192` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 192 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt192(int256 value) internal pure returns (int192) {
                                    require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits");
                                    return int192(value);
                                }
                                /**
                                 * @dev Returns the downcasted int184 from int256, reverting on
                                 * overflow (when the input is less than smallest int184 or
                                 * greater than largest int184).
                                 *
                                 * Counterpart to Solidity's `int184` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 184 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt184(int256 value) internal pure returns (int184) {
                                    require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits");
                                    return int184(value);
                                }
                                /**
                                 * @dev Returns the downcasted int176 from int256, reverting on
                                 * overflow (when the input is less than smallest int176 or
                                 * greater than largest int176).
                                 *
                                 * Counterpart to Solidity's `int176` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 176 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt176(int256 value) internal pure returns (int176) {
                                    require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits");
                                    return int176(value);
                                }
                                /**
                                 * @dev Returns the downcasted int168 from int256, reverting on
                                 * overflow (when the input is less than smallest int168 or
                                 * greater than largest int168).
                                 *
                                 * Counterpart to Solidity's `int168` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 168 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt168(int256 value) internal pure returns (int168) {
                                    require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits");
                                    return int168(value);
                                }
                                /**
                                 * @dev Returns the downcasted int160 from int256, reverting on
                                 * overflow (when the input is less than smallest int160 or
                                 * greater than largest int160).
                                 *
                                 * Counterpart to Solidity's `int160` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 160 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt160(int256 value) internal pure returns (int160) {
                                    require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits");
                                    return int160(value);
                                }
                                /**
                                 * @dev Returns the downcasted int152 from int256, reverting on
                                 * overflow (when the input is less than smallest int152 or
                                 * greater than largest int152).
                                 *
                                 * Counterpart to Solidity's `int152` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 152 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt152(int256 value) internal pure returns (int152) {
                                    require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits");
                                    return int152(value);
                                }
                                /**
                                 * @dev Returns the downcasted int144 from int256, reverting on
                                 * overflow (when the input is less than smallest int144 or
                                 * greater than largest int144).
                                 *
                                 * Counterpart to Solidity's `int144` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 144 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt144(int256 value) internal pure returns (int144) {
                                    require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits");
                                    return int144(value);
                                }
                                /**
                                 * @dev Returns the downcasted int136 from int256, reverting on
                                 * overflow (when the input is less than smallest int136 or
                                 * greater than largest int136).
                                 *
                                 * Counterpart to Solidity's `int136` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 136 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt136(int256 value) internal pure returns (int136) {
                                    require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits");
                                    return int136(value);
                                }
                                /**
                                 * @dev Returns the downcasted int128 from int256, reverting on
                                 * overflow (when the input is less than smallest int128 or
                                 * greater than largest int128).
                                 *
                                 * Counterpart to Solidity's `int128` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 128 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt128(int256 value) internal pure returns (int128) {
                                    require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
                                    return int128(value);
                                }
                                /**
                                 * @dev Returns the downcasted int120 from int256, reverting on
                                 * overflow (when the input is less than smallest int120 or
                                 * greater than largest int120).
                                 *
                                 * Counterpart to Solidity's `int120` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 120 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt120(int256 value) internal pure returns (int120) {
                                    require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits");
                                    return int120(value);
                                }
                                /**
                                 * @dev Returns the downcasted int112 from int256, reverting on
                                 * overflow (when the input is less than smallest int112 or
                                 * greater than largest int112).
                                 *
                                 * Counterpart to Solidity's `int112` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 112 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt112(int256 value) internal pure returns (int112) {
                                    require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits");
                                    return int112(value);
                                }
                                /**
                                 * @dev Returns the downcasted int104 from int256, reverting on
                                 * overflow (when the input is less than smallest int104 or
                                 * greater than largest int104).
                                 *
                                 * Counterpart to Solidity's `int104` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 104 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt104(int256 value) internal pure returns (int104) {
                                    require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits");
                                    return int104(value);
                                }
                                /**
                                 * @dev Returns the downcasted int96 from int256, reverting on
                                 * overflow (when the input is less than smallest int96 or
                                 * greater than largest int96).
                                 *
                                 * Counterpart to Solidity's `int96` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 96 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt96(int256 value) internal pure returns (int96) {
                                    require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits");
                                    return int96(value);
                                }
                                /**
                                 * @dev Returns the downcasted int88 from int256, reverting on
                                 * overflow (when the input is less than smallest int88 or
                                 * greater than largest int88).
                                 *
                                 * Counterpart to Solidity's `int88` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 88 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt88(int256 value) internal pure returns (int88) {
                                    require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits");
                                    return int88(value);
                                }
                                /**
                                 * @dev Returns the downcasted int80 from int256, reverting on
                                 * overflow (when the input is less than smallest int80 or
                                 * greater than largest int80).
                                 *
                                 * Counterpart to Solidity's `int80` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 80 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt80(int256 value) internal pure returns (int80) {
                                    require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits");
                                    return int80(value);
                                }
                                /**
                                 * @dev Returns the downcasted int72 from int256, reverting on
                                 * overflow (when the input is less than smallest int72 or
                                 * greater than largest int72).
                                 *
                                 * Counterpart to Solidity's `int72` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 72 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt72(int256 value) internal pure returns (int72) {
                                    require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits");
                                    return int72(value);
                                }
                                /**
                                 * @dev Returns the downcasted int64 from int256, reverting on
                                 * overflow (when the input is less than smallest int64 or
                                 * greater than largest int64).
                                 *
                                 * Counterpart to Solidity's `int64` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 64 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt64(int256 value) internal pure returns (int64) {
                                    require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
                                    return int64(value);
                                }
                                /**
                                 * @dev Returns the downcasted int56 from int256, reverting on
                                 * overflow (when the input is less than smallest int56 or
                                 * greater than largest int56).
                                 *
                                 * Counterpart to Solidity's `int56` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 56 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt56(int256 value) internal pure returns (int56) {
                                    require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits");
                                    return int56(value);
                                }
                                /**
                                 * @dev Returns the downcasted int48 from int256, reverting on
                                 * overflow (when the input is less than smallest int48 or
                                 * greater than largest int48).
                                 *
                                 * Counterpart to Solidity's `int48` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 48 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt48(int256 value) internal pure returns (int48) {
                                    require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits");
                                    return int48(value);
                                }
                                /**
                                 * @dev Returns the downcasted int40 from int256, reverting on
                                 * overflow (when the input is less than smallest int40 or
                                 * greater than largest int40).
                                 *
                                 * Counterpart to Solidity's `int40` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 40 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt40(int256 value) internal pure returns (int40) {
                                    require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits");
                                    return int40(value);
                                }
                                /**
                                 * @dev Returns the downcasted int32 from int256, reverting on
                                 * overflow (when the input is less than smallest int32 or
                                 * greater than largest int32).
                                 *
                                 * Counterpart to Solidity's `int32` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 32 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt32(int256 value) internal pure returns (int32) {
                                    require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
                                    return int32(value);
                                }
                                /**
                                 * @dev Returns the downcasted int24 from int256, reverting on
                                 * overflow (when the input is less than smallest int24 or
                                 * greater than largest int24).
                                 *
                                 * Counterpart to Solidity's `int24` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 24 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt24(int256 value) internal pure returns (int24) {
                                    require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits");
                                    return int24(value);
                                }
                                /**
                                 * @dev Returns the downcasted int16 from int256, reverting on
                                 * overflow (when the input is less than smallest int16 or
                                 * greater than largest int16).
                                 *
                                 * Counterpart to Solidity's `int16` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 16 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt16(int256 value) internal pure returns (int16) {
                                    require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
                                    return int16(value);
                                }
                                /**
                                 * @dev Returns the downcasted int8 from int256, reverting on
                                 * overflow (when the input is less than smallest int8 or
                                 * greater than largest int8).
                                 *
                                 * Counterpart to Solidity's `int8` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 8 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt8(int256 value) internal pure returns (int8) {
                                    require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
                                    return int8(value);
                                }
                                /**
                                 * @dev Converts an unsigned uint256 into a signed int256.
                                 *
                                 * Requirements:
                                 *
                                 * - input must be less than or equal to maxInt256.
                                 *
                                 * _Available since v3.0._
                                 */
                                function toInt256(uint256 value) internal pure returns (int256) {
                                    // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                                    require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                                    return int256(value);
                                }
                            }
                            

                            File 8 of 8: CosineInterestRateModel
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "@openzeppelin/contracts/access/Ownable.sol";
                            import "@openzeppelin/contracts/utils/math/SafeCast.sol";
                            import "@prb/math/contracts/PRBMathSD59x18.sol";
                            import "./abstract/InterestRateModelBase.sol";
                            import "./libraries/Trigonometry.sol";
                            /// @notice This contract represent interest rate model based on modulating cosine function
                            contract CosineInterestRateModel is InterestRateModelBase, Ownable {
                                using SafeCast for uint256;
                                using SafeCast for int256;
                                using PRBMathSD59x18 for int256;
                                /// @notice Type of the model
                                string public constant TYPE = "Cosine";
                                /// @notice Number one as 18-digit decimal
                                int256 private constant ONE = 1e18;
                                /// @notice Interest rate at zero utilization (as 18-digit decimal)
                                uint256 public zeroRate;
                                /// @notice Interest rate at full utilization (as 18-digit decimal)
                                uint256 public fullRate;
                                /// @notice Utilization at which minimal interest rate applies
                                uint256 public kink;
                                /// @notice Interest rate at kink utilization (as 18-digit decimal)
                                uint256 public kinkRate;
                                // EVENTS
                                // Event emitted when model's parameters are adjusted
                                event Configured(
                                    uint256 zeroRate,
                                    uint256 fullRate,
                                    uint256 kink,
                                    uint256 kinkRate
                                );
                                // CONSTRUCTOR
                                /// @notice Contract's constructor
                                /// @param zeroRate_ Zero rate value
                                /// @param fullRate_ Full rate value
                                /// @param kink_ Kink value
                                /// @param kinkRate_ Kink rate value
                                constructor(
                                    uint256 zeroRate_,
                                    uint256 fullRate_,
                                    uint256 kink_,
                                    uint256 kinkRate_
                                ) {
                                    require(kink_ <= ONE.toUint256(), "GTO");
                                    zeroRate = _perSecond(zeroRate_);
                                    fullRate = _perSecond(fullRate_);
                                    kink = kink_;
                                    kinkRate = _perSecond(kinkRate_);
                                    emit Configured(zeroRate_, fullRate_, kink_, kinkRate_);
                                }
                                // PUBLIC FUNCTIONS
                                /// @notice Function that calculates borrow interest rate for pool
                                /// @param balance Total pool balance
                                /// @param borrows Total pool borrows
                                /// @param reserves Sum of pool reserves and insurance
                                /// @return Borrow rate per second
                                function getBorrowRate(
                                    uint256 balance,
                                    uint256 borrows,
                                    uint256 reserves
                                ) public view override returns (uint256) {
                                    int256 util = utilizationRate(balance, borrows, reserves).toInt256();
                                    int256 iKink = kink.toInt256();
                                    int256 theta = util <= iKink ? int256(0) : ONE;
                                    int256 n = -ONE.div(iKink.log2());
                                    int256 cosX = util == 0
                                        ? ONE
                                        : Trigonometry.cos((2 * Trigonometry.PI).mul(util.pow(n)));
                                    int256 rate = (zeroRate.toInt256().mul(ONE + cosX).mul(ONE - theta) +
                                        fullRate.toInt256().mul(ONE + cosX).mul(theta) +
                                        kinkRate.toInt256().mul(ONE - cosX)) / 2;
                                    uint256 uRate = rate.toUint256();
                                    require(uRate <= MAX_RATE, "HMR");
                                    return uRate;
                                }
                                // RESTRICTED FUNCTIONS
                                /// @notice Owner's function to configure model's parameters
                                /// @param zeroRate_ Zero rate value
                                /// @param fullRate_ Full rate value
                                /// @param kink_ Kink value
                                /// @param kinkRate_ Kink rate value
                                function configure(
                                    uint256 zeroRate_,
                                    uint256 fullRate_,
                                    uint256 kink_,
                                    uint256 kinkRate_
                                ) external onlyOwner {
                                    require(kink_ <= ONE.toUint256(), "GTO");
                                    zeroRate = _perSecond(zeroRate_);
                                    fullRate = _perSecond(fullRate_);
                                    kink = kink_;
                                    kinkRate = _perSecond(kinkRate_);
                                    emit Configured(zeroRate_, fullRate_, kink_, kinkRate_);
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
                            pragma solidity ^0.8.0;
                            import "../utils/Context.sol";
                            /**
                             * @dev Contract module which provides a basic access control mechanism, where
                             * there is an account (an owner) that can be granted exclusive access to
                             * specific functions.
                             *
                             * By default, the owner account will be the one that deploys the contract. This
                             * can later be changed with {transferOwnership}.
                             *
                             * This module is used through inheritance. It will make available the modifier
                             * `onlyOwner`, which can be applied to your functions to restrict their use to
                             * the owner.
                             */
                            abstract contract Ownable is Context {
                                address private _owner;
                                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                                /**
                                 * @dev Initializes the contract setting the deployer as the initial owner.
                                 */
                                constructor() {
                                    _transferOwnership(_msgSender());
                                }
                                /**
                                 * @dev Throws if called by any account other than the owner.
                                 */
                                modifier onlyOwner() {
                                    _checkOwner();
                                    _;
                                }
                                /**
                                 * @dev Returns the address of the current owner.
                                 */
                                function owner() public view virtual returns (address) {
                                    return _owner;
                                }
                                /**
                                 * @dev Throws if the sender is not the owner.
                                 */
                                function _checkOwner() internal view virtual {
                                    require(owner() == _msgSender(), "Ownable: caller is not the owner");
                                }
                                /**
                                 * @dev Leaves the contract without owner. It will not be possible to call
                                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                                 *
                                 * NOTE: Renouncing ownership will leave the contract without an owner,
                                 * thereby removing any functionality that is only available to the owner.
                                 */
                                function renounceOwnership() public virtual onlyOwner {
                                    _transferOwnership(address(0));
                                }
                                /**
                                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                                 * Can only be called by the current owner.
                                 */
                                function transferOwnership(address newOwner) public virtual onlyOwner {
                                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                                    _transferOwnership(newOwner);
                                }
                                /**
                                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                                 * Internal function without access restriction.
                                 */
                                function _transferOwnership(address newOwner) internal virtual {
                                    address oldOwner = _owner;
                                    _owner = newOwner;
                                    emit OwnershipTransferred(oldOwner, newOwner);
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                             * checks.
                             *
                             * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                             * easily result in undesired exploitation or bugs, since developers usually
                             * assume that overflows raise errors. `SafeCast` restores this intuition by
                             * reverting the transaction when such an operation overflows.
                             *
                             * Using this library instead of the unchecked operations eliminates an entire
                             * class of bugs, so it's recommended to use it always.
                             *
                             * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                             * all math on `uint256` and `int256` and then downcasting.
                             */
                            library SafeCast {
                                /**
                                 * @dev Returns the downcasted uint248 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint248).
                                 *
                                 * Counterpart to Solidity's `uint248` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 248 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint248(uint256 value) internal pure returns (uint248) {
                                    require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
                                    return uint248(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint240 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint240).
                                 *
                                 * Counterpart to Solidity's `uint240` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 240 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint240(uint256 value) internal pure returns (uint240) {
                                    require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
                                    return uint240(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint232 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint232).
                                 *
                                 * Counterpart to Solidity's `uint232` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 232 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint232(uint256 value) internal pure returns (uint232) {
                                    require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
                                    return uint232(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint224 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint224).
                                 *
                                 * Counterpart to Solidity's `uint224` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 224 bits
                                 *
                                 * _Available since v4.2._
                                 */
                                function toUint224(uint256 value) internal pure returns (uint224) {
                                    require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                                    return uint224(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint216 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint216).
                                 *
                                 * Counterpart to Solidity's `uint216` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 216 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint216(uint256 value) internal pure returns (uint216) {
                                    require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
                                    return uint216(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint208 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint208).
                                 *
                                 * Counterpart to Solidity's `uint208` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 208 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint208(uint256 value) internal pure returns (uint208) {
                                    require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
                                    return uint208(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint200 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint200).
                                 *
                                 * Counterpart to Solidity's `uint200` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 200 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint200(uint256 value) internal pure returns (uint200) {
                                    require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
                                    return uint200(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint192 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint192).
                                 *
                                 * Counterpart to Solidity's `uint192` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 192 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint192(uint256 value) internal pure returns (uint192) {
                                    require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
                                    return uint192(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint184 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint184).
                                 *
                                 * Counterpart to Solidity's `uint184` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 184 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint184(uint256 value) internal pure returns (uint184) {
                                    require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
                                    return uint184(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint176 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint176).
                                 *
                                 * Counterpart to Solidity's `uint176` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 176 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint176(uint256 value) internal pure returns (uint176) {
                                    require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
                                    return uint176(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint168 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint168).
                                 *
                                 * Counterpart to Solidity's `uint168` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 168 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint168(uint256 value) internal pure returns (uint168) {
                                    require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
                                    return uint168(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint160 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint160).
                                 *
                                 * Counterpart to Solidity's `uint160` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 160 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint160(uint256 value) internal pure returns (uint160) {
                                    require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
                                    return uint160(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint152 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint152).
                                 *
                                 * Counterpart to Solidity's `uint152` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 152 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint152(uint256 value) internal pure returns (uint152) {
                                    require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
                                    return uint152(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint144 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint144).
                                 *
                                 * Counterpart to Solidity's `uint144` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 144 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint144(uint256 value) internal pure returns (uint144) {
                                    require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
                                    return uint144(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint136 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint136).
                                 *
                                 * Counterpart to Solidity's `uint136` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 136 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint136(uint256 value) internal pure returns (uint136) {
                                    require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
                                    return uint136(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint128 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint128).
                                 *
                                 * Counterpart to Solidity's `uint128` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 128 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint128(uint256 value) internal pure returns (uint128) {
                                    require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                                    return uint128(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint120 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint120).
                                 *
                                 * Counterpart to Solidity's `uint120` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 120 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint120(uint256 value) internal pure returns (uint120) {
                                    require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
                                    return uint120(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint112 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint112).
                                 *
                                 * Counterpart to Solidity's `uint112` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 112 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint112(uint256 value) internal pure returns (uint112) {
                                    require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
                                    return uint112(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint104 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint104).
                                 *
                                 * Counterpart to Solidity's `uint104` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 104 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint104(uint256 value) internal pure returns (uint104) {
                                    require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
                                    return uint104(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint96 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint96).
                                 *
                                 * Counterpart to Solidity's `uint96` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 96 bits
                                 *
                                 * _Available since v4.2._
                                 */
                                function toUint96(uint256 value) internal pure returns (uint96) {
                                    require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                                    return uint96(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint88 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint88).
                                 *
                                 * Counterpart to Solidity's `uint88` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 88 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint88(uint256 value) internal pure returns (uint88) {
                                    require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
                                    return uint88(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint80 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint80).
                                 *
                                 * Counterpart to Solidity's `uint80` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 80 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint80(uint256 value) internal pure returns (uint80) {
                                    require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
                                    return uint80(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint72 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint72).
                                 *
                                 * Counterpart to Solidity's `uint72` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 72 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint72(uint256 value) internal pure returns (uint72) {
                                    require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
                                    return uint72(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint64 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint64).
                                 *
                                 * Counterpart to Solidity's `uint64` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 64 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint64(uint256 value) internal pure returns (uint64) {
                                    require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                                    return uint64(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint56 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint56).
                                 *
                                 * Counterpart to Solidity's `uint56` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 56 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint56(uint256 value) internal pure returns (uint56) {
                                    require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
                                    return uint56(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint48 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint48).
                                 *
                                 * Counterpart to Solidity's `uint48` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 48 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint48(uint256 value) internal pure returns (uint48) {
                                    require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
                                    return uint48(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint40 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint40).
                                 *
                                 * Counterpart to Solidity's `uint40` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 40 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint40(uint256 value) internal pure returns (uint40) {
                                    require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
                                    return uint40(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint32 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint32).
                                 *
                                 * Counterpart to Solidity's `uint32` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 32 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint32(uint256 value) internal pure returns (uint32) {
                                    require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                                    return uint32(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint24 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint24).
                                 *
                                 * Counterpart to Solidity's `uint24` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 24 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toUint24(uint256 value) internal pure returns (uint24) {
                                    require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
                                    return uint24(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint16 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint16).
                                 *
                                 * Counterpart to Solidity's `uint16` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 16 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint16(uint256 value) internal pure returns (uint16) {
                                    require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                                    return uint16(value);
                                }
                                /**
                                 * @dev Returns the downcasted uint8 from uint256, reverting on
                                 * overflow (when the input is greater than largest uint8).
                                 *
                                 * Counterpart to Solidity's `uint8` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 8 bits
                                 *
                                 * _Available since v2.5._
                                 */
                                function toUint8(uint256 value) internal pure returns (uint8) {
                                    require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                                    return uint8(value);
                                }
                                /**
                                 * @dev Converts a signed int256 into an unsigned uint256.
                                 *
                                 * Requirements:
                                 *
                                 * - input must be greater than or equal to 0.
                                 *
                                 * _Available since v3.0._
                                 */
                                function toUint256(int256 value) internal pure returns (uint256) {
                                    require(value >= 0, "SafeCast: value must be positive");
                                    return uint256(value);
                                }
                                /**
                                 * @dev Returns the downcasted int248 from int256, reverting on
                                 * overflow (when the input is less than smallest int248 or
                                 * greater than largest int248).
                                 *
                                 * Counterpart to Solidity's `int248` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 248 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt248(int256 value) internal pure returns (int248) {
                                    require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits");
                                    return int248(value);
                                }
                                /**
                                 * @dev Returns the downcasted int240 from int256, reverting on
                                 * overflow (when the input is less than smallest int240 or
                                 * greater than largest int240).
                                 *
                                 * Counterpart to Solidity's `int240` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 240 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt240(int256 value) internal pure returns (int240) {
                                    require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits");
                                    return int240(value);
                                }
                                /**
                                 * @dev Returns the downcasted int232 from int256, reverting on
                                 * overflow (when the input is less than smallest int232 or
                                 * greater than largest int232).
                                 *
                                 * Counterpart to Solidity's `int232` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 232 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt232(int256 value) internal pure returns (int232) {
                                    require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits");
                                    return int232(value);
                                }
                                /**
                                 * @dev Returns the downcasted int224 from int256, reverting on
                                 * overflow (when the input is less than smallest int224 or
                                 * greater than largest int224).
                                 *
                                 * Counterpart to Solidity's `int224` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 224 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt224(int256 value) internal pure returns (int224) {
                                    require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits");
                                    return int224(value);
                                }
                                /**
                                 * @dev Returns the downcasted int216 from int256, reverting on
                                 * overflow (when the input is less than smallest int216 or
                                 * greater than largest int216).
                                 *
                                 * Counterpart to Solidity's `int216` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 216 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt216(int256 value) internal pure returns (int216) {
                                    require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits");
                                    return int216(value);
                                }
                                /**
                                 * @dev Returns the downcasted int208 from int256, reverting on
                                 * overflow (when the input is less than smallest int208 or
                                 * greater than largest int208).
                                 *
                                 * Counterpart to Solidity's `int208` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 208 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt208(int256 value) internal pure returns (int208) {
                                    require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits");
                                    return int208(value);
                                }
                                /**
                                 * @dev Returns the downcasted int200 from int256, reverting on
                                 * overflow (when the input is less than smallest int200 or
                                 * greater than largest int200).
                                 *
                                 * Counterpart to Solidity's `int200` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 200 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt200(int256 value) internal pure returns (int200) {
                                    require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits");
                                    return int200(value);
                                }
                                /**
                                 * @dev Returns the downcasted int192 from int256, reverting on
                                 * overflow (when the input is less than smallest int192 or
                                 * greater than largest int192).
                                 *
                                 * Counterpart to Solidity's `int192` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 192 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt192(int256 value) internal pure returns (int192) {
                                    require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits");
                                    return int192(value);
                                }
                                /**
                                 * @dev Returns the downcasted int184 from int256, reverting on
                                 * overflow (when the input is less than smallest int184 or
                                 * greater than largest int184).
                                 *
                                 * Counterpart to Solidity's `int184` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 184 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt184(int256 value) internal pure returns (int184) {
                                    require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits");
                                    return int184(value);
                                }
                                /**
                                 * @dev Returns the downcasted int176 from int256, reverting on
                                 * overflow (when the input is less than smallest int176 or
                                 * greater than largest int176).
                                 *
                                 * Counterpart to Solidity's `int176` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 176 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt176(int256 value) internal pure returns (int176) {
                                    require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits");
                                    return int176(value);
                                }
                                /**
                                 * @dev Returns the downcasted int168 from int256, reverting on
                                 * overflow (when the input is less than smallest int168 or
                                 * greater than largest int168).
                                 *
                                 * Counterpart to Solidity's `int168` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 168 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt168(int256 value) internal pure returns (int168) {
                                    require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits");
                                    return int168(value);
                                }
                                /**
                                 * @dev Returns the downcasted int160 from int256, reverting on
                                 * overflow (when the input is less than smallest int160 or
                                 * greater than largest int160).
                                 *
                                 * Counterpart to Solidity's `int160` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 160 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt160(int256 value) internal pure returns (int160) {
                                    require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits");
                                    return int160(value);
                                }
                                /**
                                 * @dev Returns the downcasted int152 from int256, reverting on
                                 * overflow (when the input is less than smallest int152 or
                                 * greater than largest int152).
                                 *
                                 * Counterpart to Solidity's `int152` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 152 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt152(int256 value) internal pure returns (int152) {
                                    require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits");
                                    return int152(value);
                                }
                                /**
                                 * @dev Returns the downcasted int144 from int256, reverting on
                                 * overflow (when the input is less than smallest int144 or
                                 * greater than largest int144).
                                 *
                                 * Counterpart to Solidity's `int144` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 144 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt144(int256 value) internal pure returns (int144) {
                                    require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits");
                                    return int144(value);
                                }
                                /**
                                 * @dev Returns the downcasted int136 from int256, reverting on
                                 * overflow (when the input is less than smallest int136 or
                                 * greater than largest int136).
                                 *
                                 * Counterpart to Solidity's `int136` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 136 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt136(int256 value) internal pure returns (int136) {
                                    require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits");
                                    return int136(value);
                                }
                                /**
                                 * @dev Returns the downcasted int128 from int256, reverting on
                                 * overflow (when the input is less than smallest int128 or
                                 * greater than largest int128).
                                 *
                                 * Counterpart to Solidity's `int128` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 128 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt128(int256 value) internal pure returns (int128) {
                                    require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
                                    return int128(value);
                                }
                                /**
                                 * @dev Returns the downcasted int120 from int256, reverting on
                                 * overflow (when the input is less than smallest int120 or
                                 * greater than largest int120).
                                 *
                                 * Counterpart to Solidity's `int120` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 120 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt120(int256 value) internal pure returns (int120) {
                                    require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits");
                                    return int120(value);
                                }
                                /**
                                 * @dev Returns the downcasted int112 from int256, reverting on
                                 * overflow (when the input is less than smallest int112 or
                                 * greater than largest int112).
                                 *
                                 * Counterpart to Solidity's `int112` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 112 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt112(int256 value) internal pure returns (int112) {
                                    require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits");
                                    return int112(value);
                                }
                                /**
                                 * @dev Returns the downcasted int104 from int256, reverting on
                                 * overflow (when the input is less than smallest int104 or
                                 * greater than largest int104).
                                 *
                                 * Counterpart to Solidity's `int104` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 104 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt104(int256 value) internal pure returns (int104) {
                                    require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits");
                                    return int104(value);
                                }
                                /**
                                 * @dev Returns the downcasted int96 from int256, reverting on
                                 * overflow (when the input is less than smallest int96 or
                                 * greater than largest int96).
                                 *
                                 * Counterpart to Solidity's `int96` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 96 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt96(int256 value) internal pure returns (int96) {
                                    require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits");
                                    return int96(value);
                                }
                                /**
                                 * @dev Returns the downcasted int88 from int256, reverting on
                                 * overflow (when the input is less than smallest int88 or
                                 * greater than largest int88).
                                 *
                                 * Counterpart to Solidity's `int88` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 88 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt88(int256 value) internal pure returns (int88) {
                                    require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits");
                                    return int88(value);
                                }
                                /**
                                 * @dev Returns the downcasted int80 from int256, reverting on
                                 * overflow (when the input is less than smallest int80 or
                                 * greater than largest int80).
                                 *
                                 * Counterpart to Solidity's `int80` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 80 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt80(int256 value) internal pure returns (int80) {
                                    require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits");
                                    return int80(value);
                                }
                                /**
                                 * @dev Returns the downcasted int72 from int256, reverting on
                                 * overflow (when the input is less than smallest int72 or
                                 * greater than largest int72).
                                 *
                                 * Counterpart to Solidity's `int72` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 72 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt72(int256 value) internal pure returns (int72) {
                                    require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits");
                                    return int72(value);
                                }
                                /**
                                 * @dev Returns the downcasted int64 from int256, reverting on
                                 * overflow (when the input is less than smallest int64 or
                                 * greater than largest int64).
                                 *
                                 * Counterpart to Solidity's `int64` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 64 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt64(int256 value) internal pure returns (int64) {
                                    require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
                                    return int64(value);
                                }
                                /**
                                 * @dev Returns the downcasted int56 from int256, reverting on
                                 * overflow (when the input is less than smallest int56 or
                                 * greater than largest int56).
                                 *
                                 * Counterpart to Solidity's `int56` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 56 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt56(int256 value) internal pure returns (int56) {
                                    require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits");
                                    return int56(value);
                                }
                                /**
                                 * @dev Returns the downcasted int48 from int256, reverting on
                                 * overflow (when the input is less than smallest int48 or
                                 * greater than largest int48).
                                 *
                                 * Counterpart to Solidity's `int48` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 48 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt48(int256 value) internal pure returns (int48) {
                                    require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits");
                                    return int48(value);
                                }
                                /**
                                 * @dev Returns the downcasted int40 from int256, reverting on
                                 * overflow (when the input is less than smallest int40 or
                                 * greater than largest int40).
                                 *
                                 * Counterpart to Solidity's `int40` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 40 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt40(int256 value) internal pure returns (int40) {
                                    require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits");
                                    return int40(value);
                                }
                                /**
                                 * @dev Returns the downcasted int32 from int256, reverting on
                                 * overflow (when the input is less than smallest int32 or
                                 * greater than largest int32).
                                 *
                                 * Counterpart to Solidity's `int32` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 32 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt32(int256 value) internal pure returns (int32) {
                                    require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
                                    return int32(value);
                                }
                                /**
                                 * @dev Returns the downcasted int24 from int256, reverting on
                                 * overflow (when the input is less than smallest int24 or
                                 * greater than largest int24).
                                 *
                                 * Counterpart to Solidity's `int24` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 24 bits
                                 *
                                 * _Available since v4.7._
                                 */
                                function toInt24(int256 value) internal pure returns (int24) {
                                    require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits");
                                    return int24(value);
                                }
                                /**
                                 * @dev Returns the downcasted int16 from int256, reverting on
                                 * overflow (when the input is less than smallest int16 or
                                 * greater than largest int16).
                                 *
                                 * Counterpart to Solidity's `int16` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 16 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt16(int256 value) internal pure returns (int16) {
                                    require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
                                    return int16(value);
                                }
                                /**
                                 * @dev Returns the downcasted int8 from int256, reverting on
                                 * overflow (when the input is less than smallest int8 or
                                 * greater than largest int8).
                                 *
                                 * Counterpart to Solidity's `int8` operator.
                                 *
                                 * Requirements:
                                 *
                                 * - input must fit into 8 bits
                                 *
                                 * _Available since v3.1._
                                 */
                                function toInt8(int256 value) internal pure returns (int8) {
                                    require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
                                    return int8(value);
                                }
                                /**
                                 * @dev Converts an unsigned uint256 into a signed int256.
                                 *
                                 * Requirements:
                                 *
                                 * - input must be less than or equal to maxInt256.
                                 *
                                 * _Available since v3.0._
                                 */
                                function toInt256(uint256 value) internal pure returns (int256) {
                                    // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                                    require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                                    return int256(value);
                                }
                            }
                            // SPDX-License-Identifier: Unlicense
                            pragma solidity >=0.8.4;
                            import "./PRBMath.sol";
                            /// @title PRBMathSD59x18
                            /// @author Paul Razvan Berg
                            /// @notice Smart contract library for advanced fixed-point math that works with int256 numbers considered to have 18
                            /// trailing decimals. We call this number representation signed 59.18-decimal fixed-point, since the numbers can have
                            /// a sign and there can be up to 59 digits in the integer part and up to 18 decimals in the fractional part. The numbers
                            /// are bound by the minimum and the maximum values permitted by the Solidity type int256.
                            library PRBMathSD59x18 {
                                /// @dev log2(e) as a signed 59.18-decimal fixed-point number.
                                int256 internal constant LOG2_E = 1_442695040888963407;
                                /// @dev Half the SCALE number.
                                int256 internal constant HALF_SCALE = 5e17;
                                /// @dev The maximum value a signed 59.18-decimal fixed-point number can have.
                                int256 internal constant MAX_SD59x18 =
                                    57896044618658097711785492504343953926634992332820282019728_792003956564819967;
                                /// @dev The maximum whole value a signed 59.18-decimal fixed-point number can have.
                                int256 internal constant MAX_WHOLE_SD59x18 =
                                    57896044618658097711785492504343953926634992332820282019728_000000000000000000;
                                /// @dev The minimum value a signed 59.18-decimal fixed-point number can have.
                                int256 internal constant MIN_SD59x18 =
                                    -57896044618658097711785492504343953926634992332820282019728_792003956564819968;
                                /// @dev The minimum whole value a signed 59.18-decimal fixed-point number can have.
                                int256 internal constant MIN_WHOLE_SD59x18 =
                                    -57896044618658097711785492504343953926634992332820282019728_000000000000000000;
                                /// @dev How many trailing decimals can be represented.
                                int256 internal constant SCALE = 1e18;
                                /// INTERNAL FUNCTIONS ///
                                /// @notice Calculate the absolute value of x.
                                ///
                                /// @dev Requirements:
                                /// - x must be greater than MIN_SD59x18.
                                ///
                                /// @param x The number to calculate the absolute value for.
                                /// @param result The absolute value of x.
                                function abs(int256 x) internal pure returns (int256 result) {
                                    unchecked {
                                        if (x == MIN_SD59x18) {
                                            revert PRBMathSD59x18__AbsInputTooSmall();
                                        }
                                        result = x < 0 ? -x : x;
                                    }
                                }
                                /// @notice Calculates the arithmetic average of x and y, rounding down.
                                /// @param x The first operand as a signed 59.18-decimal fixed-point number.
                                /// @param y The second operand as a signed 59.18-decimal fixed-point number.
                                /// @return result The arithmetic average as a signed 59.18-decimal fixed-point number.
                                function avg(int256 x, int256 y) internal pure returns (int256 result) {
                                    // The operations can never overflow.
                                    unchecked {
                                        int256 sum = (x >> 1) + (y >> 1);
                                        if (sum < 0) {
                                            // If at least one of x and y is odd, we add 1 to the result. This is because shifting negative numbers to the
                                            // right rounds down to infinity.
                                            assembly {
                                                result := add(sum, and(or(x, y), 1))
                                            }
                                        } else {
                                            // If both x and y are odd, we add 1 to the result. This is because if both numbers are odd, the 0.5
                                            // remainder gets truncated twice.
                                            result = sum + (x & y & 1);
                                        }
                                    }
                                }
                                /// @notice Yields the least greatest signed 59.18 decimal fixed-point number greater than or equal to x.
                                ///
                                /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
                                /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
                                ///
                                /// Requirements:
                                /// - x must be less than or equal to MAX_WHOLE_SD59x18.
                                ///
                                /// @param x The signed 59.18-decimal fixed-point number to ceil.
                                /// @param result The least integer greater than or equal to x, as a signed 58.18-decimal fixed-point number.
                                function ceil(int256 x) internal pure returns (int256 result) {
                                    if (x > MAX_WHOLE_SD59x18) {
                                        revert PRBMathSD59x18__CeilOverflow(x);
                                    }
                                    unchecked {
                                        int256 remainder = x % SCALE;
                                        if (remainder == 0) {
                                            result = x;
                                        } else {
                                            // Solidity uses C fmod style, which returns a modulus with the same sign as x.
                                            result = x - remainder;
                                            if (x > 0) {
                                                result += SCALE;
                                            }
                                        }
                                    }
                                }
                                /// @notice Divides two signed 59.18-decimal fixed-point numbers, returning a new signed 59.18-decimal fixed-point number.
                                ///
                                /// @dev Variant of "mulDiv" that works with signed numbers. Works by computing the signs and the absolute values separately.
                                ///
                                /// Requirements:
                                /// - All from "PRBMath.mulDiv".
                                /// - None of the inputs can be MIN_SD59x18.
                                /// - The denominator cannot be zero.
                                /// - The result must fit within int256.
                                ///
                                /// Caveats:
                                /// - All from "PRBMath.mulDiv".
                                ///
                                /// @param x The numerator as a signed 59.18-decimal fixed-point number.
                                /// @param y The denominator as a signed 59.18-decimal fixed-point number.
                                /// @param result The quotient as a signed 59.18-decimal fixed-point number.
                                function div(int256 x, int256 y) internal pure returns (int256 result) {
                                    if (x == MIN_SD59x18 || y == MIN_SD59x18) {
                                        revert PRBMathSD59x18__DivInputTooSmall();
                                    }
                                    // Get hold of the absolute values of x and y.
                                    uint256 ax;
                                    uint256 ay;
                                    unchecked {
                                        ax = x < 0 ? uint256(-x) : uint256(x);
                                        ay = y < 0 ? uint256(-y) : uint256(y);
                                    }
                                    // Compute the absolute value of (x*SCALE)÷y. The result must fit within int256.
                                    uint256 rAbs = PRBMath.mulDiv(ax, uint256(SCALE), ay);
                                    if (rAbs > uint256(MAX_SD59x18)) {
                                        revert PRBMathSD59x18__DivOverflow(rAbs);
                                    }
                                    // Get the signs of x and y.
                                    uint256 sx;
                                    uint256 sy;
                                    assembly {
                                        sx := sgt(x, sub(0, 1))
                                        sy := sgt(y, sub(0, 1))
                                    }
                                    // XOR over sx and sy. This is basically checking whether the inputs have the same sign. If yes, the result
                                    // should be positive. Otherwise, it should be negative.
                                    result = sx ^ sy == 1 ? -int256(rAbs) : int256(rAbs);
                                }
                                /// @notice Returns Euler's number as a signed 59.18-decimal fixed-point number.
                                /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant).
                                function e() internal pure returns (int256 result) {
                                    result = 2_718281828459045235;
                                }
                                /// @notice Calculates the natural exponent of x.
                                ///
                                /// @dev Based on the insight that e^x = 2^(x * log2(e)).
                                ///
                                /// Requirements:
                                /// - All from "log2".
                                /// - x must be less than 133.084258667509499441.
                                ///
                                /// Caveats:
                                /// - All from "exp2".
                                /// - For any x less than -41.446531673892822322, the result is zero.
                                ///
                                /// @param x The exponent as a signed 59.18-decimal fixed-point number.
                                /// @return result The result as a signed 59.18-decimal fixed-point number.
                                function exp(int256 x) internal pure returns (int256 result) {
                                    // Without this check, the value passed to "exp2" would be less than -59.794705707972522261.
                                    if (x < -41_446531673892822322) {
                                        return 0;
                                    }
                                    // Without this check, the value passed to "exp2" would be greater than 192.
                                    if (x >= 133_084258667509499441) {
                                        revert PRBMathSD59x18__ExpInputTooBig(x);
                                    }
                                    // Do the fixed-point multiplication inline to save gas.
                                    unchecked {
                                        int256 doubleScaleProduct = x * LOG2_E;
                                        result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE);
                                    }
                                }
                                /// @notice Calculates the binary exponent of x using the binary fraction method.
                                ///
                                /// @dev See https://ethereum.stackexchange.com/q/79903/24693.
                                ///
                                /// Requirements:
                                /// - x must be 192 or less.
                                /// - The result must fit within MAX_SD59x18.
                                ///
                                /// Caveats:
                                /// - For any x less than -59.794705707972522261, the result is zero.
                                ///
                                /// @param x The exponent as a signed 59.18-decimal fixed-point number.
                                /// @return result The result as a signed 59.18-decimal fixed-point number.
                                function exp2(int256 x) internal pure returns (int256 result) {
                                    // This works because 2^(-x) = 1/2^x.
                                    if (x < 0) {
                                        // 2^59.794705707972522262 is the maximum number whose inverse does not truncate down to zero.
                                        if (x < -59_794705707972522261) {
                                            return 0;
                                        }
                                        // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE.
                                        unchecked {
                                            result = 1e36 / exp2(-x);
                                        }
                                    } else {
                                        // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
                                        if (x >= 192e18) {
                                            revert PRBMathSD59x18__Exp2InputTooBig(x);
                                        }
                                        unchecked {
                                            // Convert x to the 192.64-bit fixed-point format.
                                            uint256 x192x64 = (uint256(x) << 64) / uint256(SCALE);
                                            // Safe to convert the result to int256 directly because the maximum input allowed is 192.
                                            result = int256(PRBMath.exp2(x192x64));
                                        }
                                    }
                                }
                                /// @notice Yields the greatest signed 59.18 decimal fixed-point number less than or equal to x.
                                ///
                                /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
                                /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
                                ///
                                /// Requirements:
                                /// - x must be greater than or equal to MIN_WHOLE_SD59x18.
                                ///
                                /// @param x The signed 59.18-decimal fixed-point number to floor.
                                /// @param result The greatest integer less than or equal to x, as a signed 58.18-decimal fixed-point number.
                                function floor(int256 x) internal pure returns (int256 result) {
                                    if (x < MIN_WHOLE_SD59x18) {
                                        revert PRBMathSD59x18__FloorUnderflow(x);
                                    }
                                    unchecked {
                                        int256 remainder = x % SCALE;
                                        if (remainder == 0) {
                                            result = x;
                                        } else {
                                            // Solidity uses C fmod style, which returns a modulus with the same sign as x.
                                            result = x - remainder;
                                            if (x < 0) {
                                                result -= SCALE;
                                            }
                                        }
                                    }
                                }
                                /// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right
                                /// of the radix point for negative numbers.
                                /// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part
                                /// @param x The signed 59.18-decimal fixed-point number to get the fractional part of.
                                /// @param result The fractional part of x as a signed 59.18-decimal fixed-point number.
                                function frac(int256 x) internal pure returns (int256 result) {
                                    unchecked {
                                        result = x % SCALE;
                                    }
                                }
                                /// @notice Converts a number from basic integer form to signed 59.18-decimal fixed-point representation.
                                ///
                                /// @dev Requirements:
                                /// - x must be greater than or equal to MIN_SD59x18 divided by SCALE.
                                /// - x must be less than or equal to MAX_SD59x18 divided by SCALE.
                                ///
                                /// @param x The basic integer to convert.
                                /// @param result The same number in signed 59.18-decimal fixed-point representation.
                                function fromInt(int256 x) internal pure returns (int256 result) {
                                    unchecked {
                                        if (x < MIN_SD59x18 / SCALE) {
                                            revert PRBMathSD59x18__FromIntUnderflow(x);
                                        }
                                        if (x > MAX_SD59x18 / SCALE) {
                                            revert PRBMathSD59x18__FromIntOverflow(x);
                                        }
                                        result = x * SCALE;
                                    }
                                }
                                /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down.
                                ///
                                /// @dev Requirements:
                                /// - x * y must fit within MAX_SD59x18, lest it overflows.
                                /// - x * y cannot be negative.
                                ///
                                /// @param x The first operand as a signed 59.18-decimal fixed-point number.
                                /// @param y The second operand as a signed 59.18-decimal fixed-point number.
                                /// @return result The result as a signed 59.18-decimal fixed-point number.
                                function gm(int256 x, int256 y) internal pure returns (int256 result) {
                                    if (x == 0) {
                                        return 0;
                                    }
                                    unchecked {
                                        // Checking for overflow this way is faster than letting Solidity do it.
                                        int256 xy = x * y;
                                        if (xy / x != y) {
                                            revert PRBMathSD59x18__GmOverflow(x, y);
                                        }
                                        // The product cannot be negative.
                                        if (xy < 0) {
                                            revert PRBMathSD59x18__GmNegativeProduct(x, y);
                                        }
                                        // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE
                                        // during multiplication. See the comments within the "sqrt" function.
                                        result = int256(PRBMath.sqrt(uint256(xy)));
                                    }
                                }
                                /// @notice Calculates 1 / x, rounding toward zero.
                                ///
                                /// @dev Requirements:
                                /// - x cannot be zero.
                                ///
                                /// @param x The signed 59.18-decimal fixed-point number for which to calculate the inverse.
                                /// @return result The inverse as a signed 59.18-decimal fixed-point number.
                                function inv(int256 x) internal pure returns (int256 result) {
                                    unchecked {
                                        // 1e36 is SCALE * SCALE.
                                        result = 1e36 / x;
                                    }
                                }
                                /// @notice Calculates the natural logarithm of x.
                                ///
                                /// @dev Based on the insight that ln(x) = log2(x) / log2(e).
                                ///
                                /// Requirements:
                                /// - All from "log2".
                                ///
                                /// Caveats:
                                /// - All from "log2".
                                /// - This doesn't return exactly 1 for 2718281828459045235, for that we would need more fine-grained precision.
                                ///
                                /// @param x The signed 59.18-decimal fixed-point number for which to calculate the natural logarithm.
                                /// @return result The natural logarithm as a signed 59.18-decimal fixed-point number.
                                function ln(int256 x) internal pure returns (int256 result) {
                                    // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x)
                                    // can return is 195205294292027477728.
                                    unchecked {
                                        result = (log2(x) * SCALE) / LOG2_E;
                                    }
                                }
                                /// @notice Calculates the common logarithm of x.
                                ///
                                /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common
                                /// logarithm based on the insight that log10(x) = log2(x) / log2(10).
                                ///
                                /// Requirements:
                                /// - All from "log2".
                                ///
                                /// Caveats:
                                /// - All from "log2".
                                ///
                                /// @param x The signed 59.18-decimal fixed-point number for which to calculate the common logarithm.
                                /// @return result The common logarithm as a signed 59.18-decimal fixed-point number.
                                function log10(int256 x) internal pure returns (int256 result) {
                                    if (x <= 0) {
                                        revert PRBMathSD59x18__LogInputTooSmall(x);
                                    }
                                    // Note that the "mul" in this block is the assembly mul operation, not the "mul" function defined in this contract.
                                    // prettier-ignore
                                    assembly {
                                        switch x
                                        case 1 { result := mul(SCALE, sub(0, 18)) }
                                        case 10 { result := mul(SCALE, sub(1, 18)) }
                                        case 100 { result := mul(SCALE, sub(2, 18)) }
                                        case 1000 { result := mul(SCALE, sub(3, 18)) }
                                        case 10000 { result := mul(SCALE, sub(4, 18)) }
                                        case 100000 { result := mul(SCALE, sub(5, 18)) }
                                        case 1000000 { result := mul(SCALE, sub(6, 18)) }
                                        case 10000000 { result := mul(SCALE, sub(7, 18)) }
                                        case 100000000 { result := mul(SCALE, sub(8, 18)) }
                                        case 1000000000 { result := mul(SCALE, sub(9, 18)) }
                                        case 10000000000 { result := mul(SCALE, sub(10, 18)) }
                                        case 100000000000 { result := mul(SCALE, sub(11, 18)) }
                                        case 1000000000000 { result := mul(SCALE, sub(12, 18)) }
                                        case 10000000000000 { result := mul(SCALE, sub(13, 18)) }
                                        case 100000000000000 { result := mul(SCALE, sub(14, 18)) }
                                        case 1000000000000000 { result := mul(SCALE, sub(15, 18)) }
                                        case 10000000000000000 { result := mul(SCALE, sub(16, 18)) }
                                        case 100000000000000000 { result := mul(SCALE, sub(17, 18)) }
                                        case 1000000000000000000 { result := 0 }
                                        case 10000000000000000000 { result := SCALE }
                                        case 100000000000000000000 { result := mul(SCALE, 2) }
                                        case 1000000000000000000000 { result := mul(SCALE, 3) }
                                        case 10000000000000000000000 { result := mul(SCALE, 4) }
                                        case 100000000000000000000000 { result := mul(SCALE, 5) }
                                        case 1000000000000000000000000 { result := mul(SCALE, 6) }
                                        case 10000000000000000000000000 { result := mul(SCALE, 7) }
                                        case 100000000000000000000000000 { result := mul(SCALE, 8) }
                                        case 1000000000000000000000000000 { result := mul(SCALE, 9) }
                                        case 10000000000000000000000000000 { result := mul(SCALE, 10) }
                                        case 100000000000000000000000000000 { result := mul(SCALE, 11) }
                                        case 1000000000000000000000000000000 { result := mul(SCALE, 12) }
                                        case 10000000000000000000000000000000 { result := mul(SCALE, 13) }
                                        case 100000000000000000000000000000000 { result := mul(SCALE, 14) }
                                        case 1000000000000000000000000000000000 { result := mul(SCALE, 15) }
                                        case 10000000000000000000000000000000000 { result := mul(SCALE, 16) }
                                        case 100000000000000000000000000000000000 { result := mul(SCALE, 17) }
                                        case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) }
                                        case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) }
                                        case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) }
                                        case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) }
                                        case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) }
                                        case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) }
                                        case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) }
                                        case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) }
                                        case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) }
                                        case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) }
                                        case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) }
                                        case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) }
                                        case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) }
                                        case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) }
                                        case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) }
                                        case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) }
                                        case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) }
                                        case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) }
                                        case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) }
                                        case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) }
                                        case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) }
                                        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) }
                                        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) }
                                        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) }
                                        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) }
                                        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) }
                                        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) }
                                        default {
                                            result := MAX_SD59x18
                                        }
                                    }
                                    if (result == MAX_SD59x18) {
                                        // Do the fixed-point division inline to save gas. The denominator is log2(10).
                                        unchecked {
                                            result = (log2(x) * SCALE) / 3_321928094887362347;
                                        }
                                    }
                                }
                                /// @notice Calculates the binary logarithm of x.
                                ///
                                /// @dev Based on the iterative approximation algorithm.
                                /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
                                ///
                                /// Requirements:
                                /// - x must be greater than zero.
                                ///
                                /// Caveats:
                                /// - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
                                ///
                                /// @param x The signed 59.18-decimal fixed-point number for which to calculate the binary logarithm.
                                /// @return result The binary logarithm as a signed 59.18-decimal fixed-point number.
                                function log2(int256 x) internal pure returns (int256 result) {
                                    if (x <= 0) {
                                        revert PRBMathSD59x18__LogInputTooSmall(x);
                                    }
                                    unchecked {
                                        // This works because log2(x) = -log2(1/x).
                                        int256 sign;
                                        if (x >= SCALE) {
                                            sign = 1;
                                        } else {
                                            sign = -1;
                                            // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE.
                                            assembly {
                                                x := div(1000000000000000000000000000000000000, x)
                                            }
                                        }
                                        // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
                                        uint256 n = PRBMath.mostSignificantBit(uint256(x / SCALE));
                                        // The integer part of the logarithm as a signed 59.18-decimal fixed-point number. The operation can't overflow
                                        // because n is maximum 255, SCALE is 1e18 and sign is either 1 or -1.
                                        result = int256(n) * SCALE;
                                        // This is y = x * 2^(-n).
                                        int256 y = x >> n;
                                        // If y = 1, the fractional part is zero.
                                        if (y == SCALE) {
                                            return result * sign;
                                        }
                                        // Calculate the fractional part via the iterative approximation.
                                        // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
                                        for (int256 delta = int256(HALF_SCALE); delta > 0; delta >>= 1) {
                                            y = (y * y) / SCALE;
                                            // Is y^2 > 2 and so in the range [2,4)?
                                            if (y >= 2 * SCALE) {
                                                // Add the 2^(-m) factor to the logarithm.
                                                result += delta;
                                                // Corresponds to z/2 on Wikipedia.
                                                y >>= 1;
                                            }
                                        }
                                        result *= sign;
                                    }
                                }
                                /// @notice Multiplies two signed 59.18-decimal fixed-point numbers together, returning a new signed 59.18-decimal
                                /// fixed-point number.
                                ///
                                /// @dev Variant of "mulDiv" that works with signed numbers and employs constant folding, i.e. the denominator is
                                /// always 1e18.
                                ///
                                /// Requirements:
                                /// - All from "PRBMath.mulDivFixedPoint".
                                /// - None of the inputs can be MIN_SD59x18
                                /// - The result must fit within MAX_SD59x18.
                                ///
                                /// Caveats:
                                /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
                                ///
                                /// @param x The multiplicand as a signed 59.18-decimal fixed-point number.
                                /// @param y The multiplier as a signed 59.18-decimal fixed-point number.
                                /// @return result The product as a signed 59.18-decimal fixed-point number.
                                function mul(int256 x, int256 y) internal pure returns (int256 result) {
                                    if (x == MIN_SD59x18 || y == MIN_SD59x18) {
                                        revert PRBMathSD59x18__MulInputTooSmall();
                                    }
                                    unchecked {
                                        uint256 ax;
                                        uint256 ay;
                                        ax = x < 0 ? uint256(-x) : uint256(x);
                                        ay = y < 0 ? uint256(-y) : uint256(y);
                                        uint256 rAbs = PRBMath.mulDivFixedPoint(ax, ay);
                                        if (rAbs > uint256(MAX_SD59x18)) {
                                            revert PRBMathSD59x18__MulOverflow(rAbs);
                                        }
                                        uint256 sx;
                                        uint256 sy;
                                        assembly {
                                            sx := sgt(x, sub(0, 1))
                                            sy := sgt(y, sub(0, 1))
                                        }
                                        result = sx ^ sy == 1 ? -int256(rAbs) : int256(rAbs);
                                    }
                                }
                                /// @notice Returns PI as a signed 59.18-decimal fixed-point number.
                                function pi() internal pure returns (int256 result) {
                                    result = 3_141592653589793238;
                                }
                                /// @notice Raises x to the power of y.
                                ///
                                /// @dev Based on the insight that x^y = 2^(log2(x) * y).
                                ///
                                /// Requirements:
                                /// - All from "exp2", "log2" and "mul".
                                /// - z cannot be zero.
                                ///
                                /// Caveats:
                                /// - All from "exp2", "log2" and "mul".
                                /// - Assumes 0^0 is 1.
                                ///
                                /// @param x Number to raise to given power y, as a signed 59.18-decimal fixed-point number.
                                /// @param y Exponent to raise x to, as a signed 59.18-decimal fixed-point number.
                                /// @return result x raised to power y, as a signed 59.18-decimal fixed-point number.
                                function pow(int256 x, int256 y) internal pure returns (int256 result) {
                                    if (x == 0) {
                                        result = y == 0 ? SCALE : int256(0);
                                    } else {
                                        result = exp2(mul(log2(x), y));
                                    }
                                }
                                /// @notice Raises x (signed 59.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
                                /// famous algorithm "exponentiation by squaring".
                                ///
                                /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
                                ///
                                /// Requirements:
                                /// - All from "abs" and "PRBMath.mulDivFixedPoint".
                                /// - The result must fit within MAX_SD59x18.
                                ///
                                /// Caveats:
                                /// - All from "PRBMath.mulDivFixedPoint".
                                /// - Assumes 0^0 is 1.
                                ///
                                /// @param x The base as a signed 59.18-decimal fixed-point number.
                                /// @param y The exponent as an uint256.
                                /// @return result The result as a signed 59.18-decimal fixed-point number.
                                function powu(int256 x, uint256 y) internal pure returns (int256 result) {
                                    uint256 xAbs = uint256(abs(x));
                                    // Calculate the first iteration of the loop in advance.
                                    uint256 rAbs = y & 1 > 0 ? xAbs : uint256(SCALE);
                                    // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
                                    uint256 yAux = y;
                                    for (yAux >>= 1; yAux > 0; yAux >>= 1) {
                                        xAbs = PRBMath.mulDivFixedPoint(xAbs, xAbs);
                                        // Equivalent to "y % 2 == 1" but faster.
                                        if (yAux & 1 > 0) {
                                            rAbs = PRBMath.mulDivFixedPoint(rAbs, xAbs);
                                        }
                                    }
                                    // The result must fit within the 59.18-decimal fixed-point representation.
                                    if (rAbs > uint256(MAX_SD59x18)) {
                                        revert PRBMathSD59x18__PowuOverflow(rAbs);
                                    }
                                    // Is the base negative and the exponent an odd number?
                                    bool isNegative = x < 0 && y & 1 == 1;
                                    result = isNegative ? -int256(rAbs) : int256(rAbs);
                                }
                                /// @notice Returns 1 as a signed 59.18-decimal fixed-point number.
                                function scale() internal pure returns (int256 result) {
                                    result = SCALE;
                                }
                                /// @notice Calculates the square root of x, rounding down.
                                /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
                                ///
                                /// Requirements:
                                /// - x cannot be negative.
                                /// - x must be less than MAX_SD59x18 / SCALE.
                                ///
                                /// @param x The signed 59.18-decimal fixed-point number for which to calculate the square root.
                                /// @return result The result as a signed 59.18-decimal fixed-point .
                                function sqrt(int256 x) internal pure returns (int256 result) {
                                    unchecked {
                                        if (x < 0) {
                                            revert PRBMathSD59x18__SqrtNegativeInput(x);
                                        }
                                        if (x > MAX_SD59x18 / SCALE) {
                                            revert PRBMathSD59x18__SqrtOverflow(x);
                                        }
                                        // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two signed
                                        // 59.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root).
                                        result = int256(PRBMath.sqrt(uint256(x * SCALE)));
                                    }
                                }
                                /// @notice Converts a signed 59.18-decimal fixed-point number to basic integer form, rounding down in the process.
                                /// @param x The signed 59.18-decimal fixed-point number to convert.
                                /// @return result The same number in basic integer form.
                                function toInt(int256 x) internal pure returns (int256 result) {
                                    unchecked {
                                        result = x / SCALE;
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "@prb/math/contracts/PRBMathUD60x18.sol";
                            import "../interfaces/IInterestRateModel.sol";
                            /// @notice This abstract contract represent common part for interest rate models
                            abstract contract InterestRateModelBase is IInterestRateModel {
                                using PRBMathUD60x18 for uint256;
                                /// @notice Number of seconds per year
                                uint256 public constant SECONDS_PER_YEAR = 31536000;
                                /// @notice Maximal possible rate (100% annually)
                                uint256 public constant MAX_RATE = 10**18 / SECONDS_PER_YEAR;
                                /// @notice Function that calculates utilization rate for pool
                                /// @param balance Total pool balance
                                /// @param borrows Total pool borrows
                                /// @param reserves Sum of pool reserves and insurance
                                /// @return Utilization rate
                                function utilizationRate(
                                    uint256 balance,
                                    uint256 borrows,
                                    uint256 reserves
                                ) public pure override returns (uint256) {
                                    if (borrows == 0) {
                                        return 0;
                                    }
                                    return borrows.div(balance + borrows - reserves);
                                }
                                /// @notice Function that calculates borrow interest rate for pool
                                /// @param balance Total pool balance
                                /// @param borrows Total pool borrows
                                /// @param reserves Sum of pool reserves and insurance
                                /// @return Borrow rate per second
                                function getBorrowRate(
                                    uint256 balance,
                                    uint256 borrows,
                                    uint256 reserves
                                ) public view virtual override returns (uint256);
                                /// @notice Function that calculates supply interest rate for pool
                                /// @param balance Total pool balance
                                /// @param borrows Total pool borrows
                                /// @param reserves Sum of pool reserves and insurance
                                /// @param reserveFactor Pool reserve factor
                                /// @return Supply rate per second
                                function getSupplyRate(
                                    uint256 balance,
                                    uint256 borrows,
                                    uint256 reserves,
                                    uint256 reserveFactor
                                ) external view override returns (uint256) {
                                    uint256 util = utilizationRate(balance, borrows, reserves);
                                    return
                                        util.mul(getBorrowRate(balance, borrows, reserves)).mul(
                                            PRBMathUD60x18.SCALE - reserveFactor
                                        );
                                }
                                // INTERNAL FUNCTIONS
                                /// @notice Function to convert annual rate to per second rate
                                /// @param annualRate Annual rate to convert
                                /// @return Converted rate per second
                                function _perSecond(uint256 annualRate) internal pure returns (uint256) {
                                    return annualRate / SECONDS_PER_YEAR;
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            import "@openzeppelin/contracts/utils/math/SafeCast.sol";
                            import "@prb/math/contracts/PRBMathSD59x18.sol";
                            library Trigonometry {
                                using SafeCast for uint256;
                                using PRBMathSD59x18 for int256;
                                /// @notice Value of pi as 18-digit decimal
                                int256 internal constant PI = 3141592653589793280;
                                /// @notice Default number of sine calculation iterations
                                int256 internal constant DEFAULT_ITERATIONS = 20;
                                /// @notice Computes the sin(x) to a certain number of Taylor series terms
                                /// @param x Number to compute sine for
                                /// @param iterations Number of computation iterations (more iterations - higher precision)
                                /// @return sin(x)
                                function sinLimited(int256 x, int256 iterations)
                                    internal
                                    pure
                                    returns (int256)
                                {
                                    int256 num = x;
                                    int256 denom = 1;
                                    int256 result = num;
                                    for (int256 i = 0; i < iterations; i++) {
                                        num = num.mul(x).mul(x);
                                        denom *= (2 * i + 2) * (2 * i + 3);
                                        result +=
                                            num.div(PRBMathSD59x18.fromInt(denom)) *
                                            (i % 2 == 0 ? int256(-1) : int256(1));
                                    }
                                    return result;
                                }
                                /// @notice Computes sin(x) with a sensible maximum iteration count to wait until convergence.
                                /// @notice x Number to compute sine for
                                function sin(int256 x) internal pure returns (int256) {
                                    return sinLimited(x, DEFAULT_ITERATIONS);
                                }
                                /// @notice Computes cos(x)
                                /// @notice x Number to compute cosine for
                                function cos(int256 x) internal pure returns (int256) {
                                    return sin(x + PI / 2);
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                            pragma solidity ^0.8.0;
                            /**
                             * @dev Provides information about the current execution context, including the
                             * sender of the transaction and its data. While these are generally available
                             * via msg.sender and msg.data, they should not be accessed in such a direct
                             * manner, since when dealing with meta-transactions the account sending and
                             * paying for execution may not be the actual sender (as far as an application
                             * is concerned).
                             *
                             * This contract is only required for intermediate, library-like contracts.
                             */
                            abstract contract Context {
                                function _msgSender() internal view virtual returns (address) {
                                    return msg.sender;
                                }
                                function _msgData() internal view virtual returns (bytes calldata) {
                                    return msg.data;
                                }
                            }
                            // SPDX-License-Identifier: Unlicense
                            pragma solidity >=0.8.4;
                            /// @notice Emitted when the result overflows uint256.
                            error PRBMath__MulDivFixedPointOverflow(uint256 prod1);
                            /// @notice Emitted when the result overflows uint256.
                            error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator);
                            /// @notice Emitted when one of the inputs is type(int256).min.
                            error PRBMath__MulDivSignedInputTooSmall();
                            /// @notice Emitted when the intermediary absolute result overflows int256.
                            error PRBMath__MulDivSignedOverflow(uint256 rAbs);
                            /// @notice Emitted when the input is MIN_SD59x18.
                            error PRBMathSD59x18__AbsInputTooSmall();
                            /// @notice Emitted when ceiling a number overflows SD59x18.
                            error PRBMathSD59x18__CeilOverflow(int256 x);
                            /// @notice Emitted when one of the inputs is MIN_SD59x18.
                            error PRBMathSD59x18__DivInputTooSmall();
                            /// @notice Emitted when one of the intermediary unsigned results overflows SD59x18.
                            error PRBMathSD59x18__DivOverflow(uint256 rAbs);
                            /// @notice Emitted when the input is greater than 133.084258667509499441.
                            error PRBMathSD59x18__ExpInputTooBig(int256 x);
                            /// @notice Emitted when the input is greater than 192.
                            error PRBMathSD59x18__Exp2InputTooBig(int256 x);
                            /// @notice Emitted when flooring a number underflows SD59x18.
                            error PRBMathSD59x18__FloorUnderflow(int256 x);
                            /// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18.
                            error PRBMathSD59x18__FromIntOverflow(int256 x);
                            /// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18.
                            error PRBMathSD59x18__FromIntUnderflow(int256 x);
                            /// @notice Emitted when the product of the inputs is negative.
                            error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y);
                            /// @notice Emitted when multiplying the inputs overflows SD59x18.
                            error PRBMathSD59x18__GmOverflow(int256 x, int256 y);
                            /// @notice Emitted when the input is less than or equal to zero.
                            error PRBMathSD59x18__LogInputTooSmall(int256 x);
                            /// @notice Emitted when one of the inputs is MIN_SD59x18.
                            error PRBMathSD59x18__MulInputTooSmall();
                            /// @notice Emitted when the intermediary absolute result overflows SD59x18.
                            error PRBMathSD59x18__MulOverflow(uint256 rAbs);
                            /// @notice Emitted when the intermediary absolute result overflows SD59x18.
                            error PRBMathSD59x18__PowuOverflow(uint256 rAbs);
                            /// @notice Emitted when the input is negative.
                            error PRBMathSD59x18__SqrtNegativeInput(int256 x);
                            /// @notice Emitted when the calculating the square root overflows SD59x18.
                            error PRBMathSD59x18__SqrtOverflow(int256 x);
                            /// @notice Emitted when addition overflows UD60x18.
                            error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y);
                            /// @notice Emitted when ceiling a number overflows UD60x18.
                            error PRBMathUD60x18__CeilOverflow(uint256 x);
                            /// @notice Emitted when the input is greater than 133.084258667509499441.
                            error PRBMathUD60x18__ExpInputTooBig(uint256 x);
                            /// @notice Emitted when the input is greater than 192.
                            error PRBMathUD60x18__Exp2InputTooBig(uint256 x);
                            /// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18.
                            error PRBMathUD60x18__FromUintOverflow(uint256 x);
                            /// @notice Emitted when multiplying the inputs overflows UD60x18.
                            error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y);
                            /// @notice Emitted when the input is less than 1.
                            error PRBMathUD60x18__LogInputTooSmall(uint256 x);
                            /// @notice Emitted when the calculating the square root overflows UD60x18.
                            error PRBMathUD60x18__SqrtOverflow(uint256 x);
                            /// @notice Emitted when subtraction underflows UD60x18.
                            error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y);
                            /// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library
                            /// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point
                            /// representation. When it does not, it is explicitly mentioned in the NatSpec documentation.
                            library PRBMath {
                                /// STRUCTS ///
                                struct SD59x18 {
                                    int256 value;
                                }
                                struct UD60x18 {
                                    uint256 value;
                                }
                                /// STORAGE ///
                                /// @dev How many trailing decimals can be represented.
                                uint256 internal constant SCALE = 1e18;
                                /// @dev Largest power of two divisor of SCALE.
                                uint256 internal constant SCALE_LPOTD = 262144;
                                /// @dev SCALE inverted mod 2^256.
                                uint256 internal constant SCALE_INVERSE =
                                    78156646155174841979727994598816262306175212592076161876661_508869554232690281;
                                /// FUNCTIONS ///
                                /// @notice Calculates the binary exponent of x using the binary fraction method.
                                /// @dev Has to use 192.64-bit fixed-point numbers.
                                /// See https://ethereum.stackexchange.com/a/96594/24693.
                                /// @param x The exponent as an unsigned 192.64-bit fixed-point number.
                                /// @return result The result as an unsigned 60.18-decimal fixed-point number.
                                function exp2(uint256 x) internal pure returns (uint256 result) {
                                    unchecked {
                                        // Start from 0.5 in the 192.64-bit fixed-point format.
                                        result = 0x800000000000000000000000000000000000000000000000;
                                        // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows
                                        // because the initial result is 2^191 and all magic factors are less than 2^65.
                                        if (x & 0x8000000000000000 > 0) {
                                            result = (result * 0x16A09E667F3BCC909) >> 64;
                                        }
                                        if (x & 0x4000000000000000 > 0) {
                                            result = (result * 0x1306FE0A31B7152DF) >> 64;
                                        }
                                        if (x & 0x2000000000000000 > 0) {
                                            result = (result * 0x1172B83C7D517ADCE) >> 64;
                                        }
                                        if (x & 0x1000000000000000 > 0) {
                                            result = (result * 0x10B5586CF9890F62A) >> 64;
                                        }
                                        if (x & 0x800000000000000 > 0) {
                                            result = (result * 0x1059B0D31585743AE) >> 64;
                                        }
                                        if (x & 0x400000000000000 > 0) {
                                            result = (result * 0x102C9A3E778060EE7) >> 64;
                                        }
                                        if (x & 0x200000000000000 > 0) {
                                            result = (result * 0x10163DA9FB33356D8) >> 64;
                                        }
                                        if (x & 0x100000000000000 > 0) {
                                            result = (result * 0x100B1AFA5ABCBED61) >> 64;
                                        }
                                        if (x & 0x80000000000000 > 0) {
                                            result = (result * 0x10058C86DA1C09EA2) >> 64;
                                        }
                                        if (x & 0x40000000000000 > 0) {
                                            result = (result * 0x1002C605E2E8CEC50) >> 64;
                                        }
                                        if (x & 0x20000000000000 > 0) {
                                            result = (result * 0x100162F3904051FA1) >> 64;
                                        }
                                        if (x & 0x10000000000000 > 0) {
                                            result = (result * 0x1000B175EFFDC76BA) >> 64;
                                        }
                                        if (x & 0x8000000000000 > 0) {
                                            result = (result * 0x100058BA01FB9F96D) >> 64;
                                        }
                                        if (x & 0x4000000000000 > 0) {
                                            result = (result * 0x10002C5CC37DA9492) >> 64;
                                        }
                                        if (x & 0x2000000000000 > 0) {
                                            result = (result * 0x1000162E525EE0547) >> 64;
                                        }
                                        if (x & 0x1000000000000 > 0) {
                                            result = (result * 0x10000B17255775C04) >> 64;
                                        }
                                        if (x & 0x800000000000 > 0) {
                                            result = (result * 0x1000058B91B5BC9AE) >> 64;
                                        }
                                        if (x & 0x400000000000 > 0) {
                                            result = (result * 0x100002C5C89D5EC6D) >> 64;
                                        }
                                        if (x & 0x200000000000 > 0) {
                                            result = (result * 0x10000162E43F4F831) >> 64;
                                        }
                                        if (x & 0x100000000000 > 0) {
                                            result = (result * 0x100000B1721BCFC9A) >> 64;
                                        }
                                        if (x & 0x80000000000 > 0) {
                                            result = (result * 0x10000058B90CF1E6E) >> 64;
                                        }
                                        if (x & 0x40000000000 > 0) {
                                            result = (result * 0x1000002C5C863B73F) >> 64;
                                        }
                                        if (x & 0x20000000000 > 0) {
                                            result = (result * 0x100000162E430E5A2) >> 64;
                                        }
                                        if (x & 0x10000000000 > 0) {
                                            result = (result * 0x1000000B172183551) >> 64;
                                        }
                                        if (x & 0x8000000000 > 0) {
                                            result = (result * 0x100000058B90C0B49) >> 64;
                                        }
                                        if (x & 0x4000000000 > 0) {
                                            result = (result * 0x10000002C5C8601CC) >> 64;
                                        }
                                        if (x & 0x2000000000 > 0) {
                                            result = (result * 0x1000000162E42FFF0) >> 64;
                                        }
                                        if (x & 0x1000000000 > 0) {
                                            result = (result * 0x10000000B17217FBB) >> 64;
                                        }
                                        if (x & 0x800000000 > 0) {
                                            result = (result * 0x1000000058B90BFCE) >> 64;
                                        }
                                        if (x & 0x400000000 > 0) {
                                            result = (result * 0x100000002C5C85FE3) >> 64;
                                        }
                                        if (x & 0x200000000 > 0) {
                                            result = (result * 0x10000000162E42FF1) >> 64;
                                        }
                                        if (x & 0x100000000 > 0) {
                                            result = (result * 0x100000000B17217F8) >> 64;
                                        }
                                        if (x & 0x80000000 > 0) {
                                            result = (result * 0x10000000058B90BFC) >> 64;
                                        }
                                        if (x & 0x40000000 > 0) {
                                            result = (result * 0x1000000002C5C85FE) >> 64;
                                        }
                                        if (x & 0x20000000 > 0) {
                                            result = (result * 0x100000000162E42FF) >> 64;
                                        }
                                        if (x & 0x10000000 > 0) {
                                            result = (result * 0x1000000000B17217F) >> 64;
                                        }
                                        if (x & 0x8000000 > 0) {
                                            result = (result * 0x100000000058B90C0) >> 64;
                                        }
                                        if (x & 0x4000000 > 0) {
                                            result = (result * 0x10000000002C5C860) >> 64;
                                        }
                                        if (x & 0x2000000 > 0) {
                                            result = (result * 0x1000000000162E430) >> 64;
                                        }
                                        if (x & 0x1000000 > 0) {
                                            result = (result * 0x10000000000B17218) >> 64;
                                        }
                                        if (x & 0x800000 > 0) {
                                            result = (result * 0x1000000000058B90C) >> 64;
                                        }
                                        if (x & 0x400000 > 0) {
                                            result = (result * 0x100000000002C5C86) >> 64;
                                        }
                                        if (x & 0x200000 > 0) {
                                            result = (result * 0x10000000000162E43) >> 64;
                                        }
                                        if (x & 0x100000 > 0) {
                                            result = (result * 0x100000000000B1721) >> 64;
                                        }
                                        if (x & 0x80000 > 0) {
                                            result = (result * 0x10000000000058B91) >> 64;
                                        }
                                        if (x & 0x40000 > 0) {
                                            result = (result * 0x1000000000002C5C8) >> 64;
                                        }
                                        if (x & 0x20000 > 0) {
                                            result = (result * 0x100000000000162E4) >> 64;
                                        }
                                        if (x & 0x10000 > 0) {
                                            result = (result * 0x1000000000000B172) >> 64;
                                        }
                                        if (x & 0x8000 > 0) {
                                            result = (result * 0x100000000000058B9) >> 64;
                                        }
                                        if (x & 0x4000 > 0) {
                                            result = (result * 0x10000000000002C5D) >> 64;
                                        }
                                        if (x & 0x2000 > 0) {
                                            result = (result * 0x1000000000000162E) >> 64;
                                        }
                                        if (x & 0x1000 > 0) {
                                            result = (result * 0x10000000000000B17) >> 64;
                                        }
                                        if (x & 0x800 > 0) {
                                            result = (result * 0x1000000000000058C) >> 64;
                                        }
                                        if (x & 0x400 > 0) {
                                            result = (result * 0x100000000000002C6) >> 64;
                                        }
                                        if (x & 0x200 > 0) {
                                            result = (result * 0x10000000000000163) >> 64;
                                        }
                                        if (x & 0x100 > 0) {
                                            result = (result * 0x100000000000000B1) >> 64;
                                        }
                                        if (x & 0x80 > 0) {
                                            result = (result * 0x10000000000000059) >> 64;
                                        }
                                        if (x & 0x40 > 0) {
                                            result = (result * 0x1000000000000002C) >> 64;
                                        }
                                        if (x & 0x20 > 0) {
                                            result = (result * 0x10000000000000016) >> 64;
                                        }
                                        if (x & 0x10 > 0) {
                                            result = (result * 0x1000000000000000B) >> 64;
                                        }
                                        if (x & 0x8 > 0) {
                                            result = (result * 0x10000000000000006) >> 64;
                                        }
                                        if (x & 0x4 > 0) {
                                            result = (result * 0x10000000000000003) >> 64;
                                        }
                                        if (x & 0x2 > 0) {
                                            result = (result * 0x10000000000000001) >> 64;
                                        }
                                        if (x & 0x1 > 0) {
                                            result = (result * 0x10000000000000001) >> 64;
                                        }
                                        // We're doing two things at the same time:
                                        //
                                        //   1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for
                                        //      the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191
                                        //      rather than 192.
                                        //   2. Convert the result to the unsigned 60.18-decimal fixed-point format.
                                        //
                                        // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n".
                                        result *= SCALE;
                                        result >>= (191 - (x >> 64));
                                    }
                                }
                                /// @notice Finds the zero-based index of the first one in the binary representation of x.
                                /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
                                /// @param x The uint256 number for which to find the index of the most significant bit.
                                /// @return msb The index of the most significant bit as an uint256.
                                function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) {
                                    if (x >= 2**128) {
                                        x >>= 128;
                                        msb += 128;
                                    }
                                    if (x >= 2**64) {
                                        x >>= 64;
                                        msb += 64;
                                    }
                                    if (x >= 2**32) {
                                        x >>= 32;
                                        msb += 32;
                                    }
                                    if (x >= 2**16) {
                                        x >>= 16;
                                        msb += 16;
                                    }
                                    if (x >= 2**8) {
                                        x >>= 8;
                                        msb += 8;
                                    }
                                    if (x >= 2**4) {
                                        x >>= 4;
                                        msb += 4;
                                    }
                                    if (x >= 2**2) {
                                        x >>= 2;
                                        msb += 2;
                                    }
                                    if (x >= 2**1) {
                                        // No need to shift x any more.
                                        msb += 1;
                                    }
                                }
                                /// @notice Calculates floor(x*y÷denominator) with full precision.
                                ///
                                /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
                                ///
                                /// Requirements:
                                /// - The denominator cannot be zero.
                                /// - The result must fit within uint256.
                                ///
                                /// Caveats:
                                /// - This function does not work with fixed-point numbers.
                                ///
                                /// @param x The multiplicand as an uint256.
                                /// @param y The multiplier as an uint256.
                                /// @param denominator The divisor as an uint256.
                                /// @return result The result as an uint256.
                                function mulDiv(
                                    uint256 x,
                                    uint256 y,
                                    uint256 denominator
                                ) internal pure returns (uint256 result) {
                                    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                    // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                    // variables such that product = prod1 * 2^256 + prod0.
                                    uint256 prod0; // Least significant 256 bits of the product
                                    uint256 prod1; // Most significant 256 bits of the product
                                    assembly {
                                        let mm := mulmod(x, y, not(0))
                                        prod0 := mul(x, y)
                                        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                    }
                                    // Handle non-overflow cases, 256 by 256 division.
                                    if (prod1 == 0) {
                                        unchecked {
                                            result = prod0 / denominator;
                                        }
                                        return result;
                                    }
                                    // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                    if (prod1 >= denominator) {
                                        revert PRBMath__MulDivOverflow(prod1, denominator);
                                    }
                                    ///////////////////////////////////////////////
                                    // 512 by 256 division.
                                    ///////////////////////////////////////////////
                                    // Make division exact by subtracting the remainder from [prod1 prod0].
                                    uint256 remainder;
                                    assembly {
                                        // Compute remainder using mulmod.
                                        remainder := mulmod(x, y, denominator)
                                        // Subtract 256 bit number from 512 bit number.
                                        prod1 := sub(prod1, gt(remainder, prod0))
                                        prod0 := sub(prod0, remainder)
                                    }
                                    // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                    // See https://cs.stackexchange.com/q/138556/92363.
                                    unchecked {
                                        // Does not overflow because the denominator cannot be zero at this stage in the function.
                                        uint256 lpotdod = denominator & (~denominator + 1);
                                        assembly {
                                            // Divide denominator by lpotdod.
                                            denominator := div(denominator, lpotdod)
                                            // Divide [prod1 prod0] by lpotdod.
                                            prod0 := div(prod0, lpotdod)
                                            // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one.
                                            lpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
                                        }
                                        // Shift in bits from prod1 into prod0.
                                        prod0 |= prod1 * lpotdod;
                                        // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                        // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                        // four bits. That is, denominator * inv = 1 mod 2^4.
                                        uint256 inverse = (3 * denominator) ^ 2;
                                        // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                        // in modular arithmetic, doubling the correct bits in each step.
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                        inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                        // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                        // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                        // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                        // is no longer required.
                                        result = prod0 * inverse;
                                        return result;
                                    }
                                }
                                /// @notice Calculates floor(x*y÷1e18) with full precision.
                                ///
                                /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the
                                /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of
                                /// being rounded to 1e-18.  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717.
                                ///
                                /// Requirements:
                                /// - The result must fit within uint256.
                                ///
                                /// Caveats:
                                /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
                                /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations:
                                ///     1. x * y = type(uint256).max * SCALE
                                ///     2. (x * y) % SCALE >= SCALE / 2
                                ///
                                /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
                                /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
                                /// @return result The result as an unsigned 60.18-decimal fixed-point number.
                                function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) {
                                    uint256 prod0;
                                    uint256 prod1;
                                    assembly {
                                        let mm := mulmod(x, y, not(0))
                                        prod0 := mul(x, y)
                                        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                    }
                                    if (prod1 >= SCALE) {
                                        revert PRBMath__MulDivFixedPointOverflow(prod1);
                                    }
                                    uint256 remainder;
                                    uint256 roundUpUnit;
                                    assembly {
                                        remainder := mulmod(x, y, SCALE)
                                        roundUpUnit := gt(remainder, 499999999999999999)
                                    }
                                    if (prod1 == 0) {
                                        unchecked {
                                            result = (prod0 / SCALE) + roundUpUnit;
                                            return result;
                                        }
                                    }
                                    assembly {
                                        result := add(
                                            mul(
                                                or(
                                                    div(sub(prod0, remainder), SCALE_LPOTD),
                                                    mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1))
                                                ),
                                                SCALE_INVERSE
                                            ),
                                            roundUpUnit
                                        )
                                    }
                                }
                                /// @notice Calculates floor(x*y÷denominator) with full precision.
                                ///
                                /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately.
                                ///
                                /// Requirements:
                                /// - None of the inputs can be type(int256).min.
                                /// - The result must fit within int256.
                                ///
                                /// @param x The multiplicand as an int256.
                                /// @param y The multiplier as an int256.
                                /// @param denominator The divisor as an int256.
                                /// @return result The result as an int256.
                                function mulDivSigned(
                                    int256 x,
                                    int256 y,
                                    int256 denominator
                                ) internal pure returns (int256 result) {
                                    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
                                        revert PRBMath__MulDivSignedInputTooSmall();
                                    }
                                    // Get hold of the absolute values of x, y and the denominator.
                                    uint256 ax;
                                    uint256 ay;
                                    uint256 ad;
                                    unchecked {
                                        ax = x < 0 ? uint256(-x) : uint256(x);
                                        ay = y < 0 ? uint256(-y) : uint256(y);
                                        ad = denominator < 0 ? uint256(-denominator) : uint256(denominator);
                                    }
                                    // Compute the absolute value of (x*y)÷denominator. The result must fit within int256.
                                    uint256 rAbs = mulDiv(ax, ay, ad);
                                    if (rAbs > uint256(type(int256).max)) {
                                        revert PRBMath__MulDivSignedOverflow(rAbs);
                                    }
                                    // Get the signs of x, y and the denominator.
                                    uint256 sx;
                                    uint256 sy;
                                    uint256 sd;
                                    assembly {
                                        sx := sgt(x, sub(0, 1))
                                        sy := sgt(y, sub(0, 1))
                                        sd := sgt(denominator, sub(0, 1))
                                    }
                                    // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs.
                                    // If yes, the result should be negative.
                                    result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs);
                                }
                                /// @notice Calculates the square root of x, rounding down.
                                /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
                                ///
                                /// Caveats:
                                /// - This function does not work with fixed-point numbers.
                                ///
                                /// @param x The uint256 number for which to calculate the square root.
                                /// @return result The result as an uint256.
                                function sqrt(uint256 x) internal pure returns (uint256 result) {
                                    if (x == 0) {
                                        return 0;
                                    }
                                    // Set the initial guess to the least power of two that is greater than or equal to sqrt(x).
                                    uint256 xAux = uint256(x);
                                    result = 1;
                                    if (xAux >= 0x100000000000000000000000000000000) {
                                        xAux >>= 128;
                                        result <<= 64;
                                    }
                                    if (xAux >= 0x10000000000000000) {
                                        xAux >>= 64;
                                        result <<= 32;
                                    }
                                    if (xAux >= 0x100000000) {
                                        xAux >>= 32;
                                        result <<= 16;
                                    }
                                    if (xAux >= 0x10000) {
                                        xAux >>= 16;
                                        result <<= 8;
                                    }
                                    if (xAux >= 0x100) {
                                        xAux >>= 8;
                                        result <<= 4;
                                    }
                                    if (xAux >= 0x10) {
                                        xAux >>= 4;
                                        result <<= 2;
                                    }
                                    if (xAux >= 0x8) {
                                        result <<= 1;
                                    }
                                    // The operations can never overflow because the result is max 2^127 when it enters this block.
                                    unchecked {
                                        result = (result + x / result) >> 1;
                                        result = (result + x / result) >> 1;
                                        result = (result + x / result) >> 1;
                                        result = (result + x / result) >> 1;
                                        result = (result + x / result) >> 1;
                                        result = (result + x / result) >> 1;
                                        result = (result + x / result) >> 1; // Seven iterations should be enough
                                        uint256 roundedDownResult = x / result;
                                        return result >= roundedDownResult ? roundedDownResult : result;
                                    }
                                }
                            }
                            // SPDX-License-Identifier: Unlicense
                            pragma solidity >=0.8.4;
                            import "./PRBMath.sol";
                            /// @title PRBMathUD60x18
                            /// @author Paul Razvan Berg
                            /// @notice Smart contract library for advanced fixed-point math that works with uint256 numbers considered to have 18
                            /// trailing decimals. We call this number representation unsigned 60.18-decimal fixed-point, since there can be up to 60
                            /// digits in the integer part and up to 18 decimals in the fractional part. The numbers are bound by the minimum and the
                            /// maximum values permitted by the Solidity type uint256.
                            library PRBMathUD60x18 {
                                /// @dev Half the SCALE number.
                                uint256 internal constant HALF_SCALE = 5e17;
                                /// @dev log2(e) as an unsigned 60.18-decimal fixed-point number.
                                uint256 internal constant LOG2_E = 1_442695040888963407;
                                /// @dev The maximum value an unsigned 60.18-decimal fixed-point number can have.
                                uint256 internal constant MAX_UD60x18 =
                                    115792089237316195423570985008687907853269984665640564039457_584007913129639935;
                                /// @dev The maximum whole value an unsigned 60.18-decimal fixed-point number can have.
                                uint256 internal constant MAX_WHOLE_UD60x18 =
                                    115792089237316195423570985008687907853269984665640564039457_000000000000000000;
                                /// @dev How many trailing decimals can be represented.
                                uint256 internal constant SCALE = 1e18;
                                /// @notice Calculates the arithmetic average of x and y, rounding down.
                                /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
                                /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
                                /// @return result The arithmetic average as an unsigned 60.18-decimal fixed-point number.
                                function avg(uint256 x, uint256 y) internal pure returns (uint256 result) {
                                    // The operations can never overflow.
                                    unchecked {
                                        // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need
                                        // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice.
                                        result = (x >> 1) + (y >> 1) + (x & y & 1);
                                    }
                                }
                                /// @notice Yields the least unsigned 60.18 decimal fixed-point number greater than or equal to x.
                                ///
                                /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
                                /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
                                ///
                                /// Requirements:
                                /// - x must be less than or equal to MAX_WHOLE_UD60x18.
                                ///
                                /// @param x The unsigned 60.18-decimal fixed-point number to ceil.
                                /// @param result The least integer greater than or equal to x, as an unsigned 60.18-decimal fixed-point number.
                                function ceil(uint256 x) internal pure returns (uint256 result) {
                                    if (x > MAX_WHOLE_UD60x18) {
                                        revert PRBMathUD60x18__CeilOverflow(x);
                                    }
                                    assembly {
                                        // Equivalent to "x % SCALE" but faster.
                                        let remainder := mod(x, SCALE)
                                        // Equivalent to "SCALE - remainder" but faster.
                                        let delta := sub(SCALE, remainder)
                                        // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster.
                                        result := add(x, mul(delta, gt(remainder, 0)))
                                    }
                                }
                                /// @notice Divides two unsigned 60.18-decimal fixed-point numbers, returning a new unsigned 60.18-decimal fixed-point number.
                                ///
                                /// @dev Uses mulDiv to enable overflow-safe multiplication and division.
                                ///
                                /// Requirements:
                                /// - The denominator cannot be zero.
                                ///
                                /// @param x The numerator as an unsigned 60.18-decimal fixed-point number.
                                /// @param y The denominator as an unsigned 60.18-decimal fixed-point number.
                                /// @param result The quotient as an unsigned 60.18-decimal fixed-point number.
                                function div(uint256 x, uint256 y) internal pure returns (uint256 result) {
                                    result = PRBMath.mulDiv(x, SCALE, y);
                                }
                                /// @notice Returns Euler's number as an unsigned 60.18-decimal fixed-point number.
                                /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant).
                                function e() internal pure returns (uint256 result) {
                                    result = 2_718281828459045235;
                                }
                                /// @notice Calculates the natural exponent of x.
                                ///
                                /// @dev Based on the insight that e^x = 2^(x * log2(e)).
                                ///
                                /// Requirements:
                                /// - All from "log2".
                                /// - x must be less than 133.084258667509499441.
                                ///
                                /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
                                /// @return result The result as an unsigned 60.18-decimal fixed-point number.
                                function exp(uint256 x) internal pure returns (uint256 result) {
                                    // Without this check, the value passed to "exp2" would be greater than 192.
                                    if (x >= 133_084258667509499441) {
                                        revert PRBMathUD60x18__ExpInputTooBig(x);
                                    }
                                    // Do the fixed-point multiplication inline to save gas.
                                    unchecked {
                                        uint256 doubleScaleProduct = x * LOG2_E;
                                        result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE);
                                    }
                                }
                                /// @notice Calculates the binary exponent of x using the binary fraction method.
                                ///
                                /// @dev See https://ethereum.stackexchange.com/q/79903/24693.
                                ///
                                /// Requirements:
                                /// - x must be 192 or less.
                                /// - The result must fit within MAX_UD60x18.
                                ///
                                /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
                                /// @return result The result as an unsigned 60.18-decimal fixed-point number.
                                function exp2(uint256 x) internal pure returns (uint256 result) {
                                    // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
                                    if (x >= 192e18) {
                                        revert PRBMathUD60x18__Exp2InputTooBig(x);
                                    }
                                    unchecked {
                                        // Convert x to the 192.64-bit fixed-point format.
                                        uint256 x192x64 = (x << 64) / SCALE;
                                        // Pass x to the PRBMath.exp2 function, which uses the 192.64-bit fixed-point number representation.
                                        result = PRBMath.exp2(x192x64);
                                    }
                                }
                                /// @notice Yields the greatest unsigned 60.18 decimal fixed-point number less than or equal to x.
                                /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
                                /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
                                /// @param x The unsigned 60.18-decimal fixed-point number to floor.
                                /// @param result The greatest integer less than or equal to x, as an unsigned 60.18-decimal fixed-point number.
                                function floor(uint256 x) internal pure returns (uint256 result) {
                                    assembly {
                                        // Equivalent to "x % SCALE" but faster.
                                        let remainder := mod(x, SCALE)
                                        // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster.
                                        result := sub(x, mul(remainder, gt(remainder, 0)))
                                    }
                                }
                                /// @notice Yields the excess beyond the floor of x.
                                /// @dev Based on the odd function definition https://en.wikipedia.org/wiki/Fractional_part.
                                /// @param x The unsigned 60.18-decimal fixed-point number to get the fractional part of.
                                /// @param result The fractional part of x as an unsigned 60.18-decimal fixed-point number.
                                function frac(uint256 x) internal pure returns (uint256 result) {
                                    assembly {
                                        result := mod(x, SCALE)
                                    }
                                }
                                /// @notice Converts a number from basic integer form to unsigned 60.18-decimal fixed-point representation.
                                ///
                                /// @dev Requirements:
                                /// - x must be less than or equal to MAX_UD60x18 divided by SCALE.
                                ///
                                /// @param x The basic integer to convert.
                                /// @param result The same number in unsigned 60.18-decimal fixed-point representation.
                                function fromUint(uint256 x) internal pure returns (uint256 result) {
                                    unchecked {
                                        if (x > MAX_UD60x18 / SCALE) {
                                            revert PRBMathUD60x18__FromUintOverflow(x);
                                        }
                                        result = x * SCALE;
                                    }
                                }
                                /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down.
                                ///
                                /// @dev Requirements:
                                /// - x * y must fit within MAX_UD60x18, lest it overflows.
                                ///
                                /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
                                /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
                                /// @return result The result as an unsigned 60.18-decimal fixed-point number.
                                function gm(uint256 x, uint256 y) internal pure returns (uint256 result) {
                                    if (x == 0) {
                                        return 0;
                                    }
                                    unchecked {
                                        // Checking for overflow this way is faster than letting Solidity do it.
                                        uint256 xy = x * y;
                                        if (xy / x != y) {
                                            revert PRBMathUD60x18__GmOverflow(x, y);
                                        }
                                        // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE
                                        // during multiplication. See the comments within the "sqrt" function.
                                        result = PRBMath.sqrt(xy);
                                    }
                                }
                                /// @notice Calculates 1 / x, rounding toward zero.
                                ///
                                /// @dev Requirements:
                                /// - x cannot be zero.
                                ///
                                /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the inverse.
                                /// @return result The inverse as an unsigned 60.18-decimal fixed-point number.
                                function inv(uint256 x) internal pure returns (uint256 result) {
                                    unchecked {
                                        // 1e36 is SCALE * SCALE.
                                        result = 1e36 / x;
                                    }
                                }
                                /// @notice Calculates the natural logarithm of x.
                                ///
                                /// @dev Based on the insight that ln(x) = log2(x) / log2(e).
                                ///
                                /// Requirements:
                                /// - All from "log2".
                                ///
                                /// Caveats:
                                /// - All from "log2".
                                /// - This doesn't return exactly 1 for 2.718281828459045235, for that we would need more fine-grained precision.
                                ///
                                /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the natural logarithm.
                                /// @return result The natural logarithm as an unsigned 60.18-decimal fixed-point number.
                                function ln(uint256 x) internal pure returns (uint256 result) {
                                    // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x)
                                    // can return is 196205294292027477728.
                                    unchecked {
                                        result = (log2(x) * SCALE) / LOG2_E;
                                    }
                                }
                                /// @notice Calculates the common logarithm of x.
                                ///
                                /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common
                                /// logarithm based on the insight that log10(x) = log2(x) / log2(10).
                                ///
                                /// Requirements:
                                /// - All from "log2".
                                ///
                                /// Caveats:
                                /// - All from "log2".
                                ///
                                /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the common logarithm.
                                /// @return result The common logarithm as an unsigned 60.18-decimal fixed-point number.
                                function log10(uint256 x) internal pure returns (uint256 result) {
                                    if (x < SCALE) {
                                        revert PRBMathUD60x18__LogInputTooSmall(x);
                                    }
                                    // Note that the "mul" in this block is the assembly multiplication operation, not the "mul" function defined
                                    // in this contract.
                                    // prettier-ignore
                                    assembly {
                                        switch x
                                        case 1 { result := mul(SCALE, sub(0, 18)) }
                                        case 10 { result := mul(SCALE, sub(1, 18)) }
                                        case 100 { result := mul(SCALE, sub(2, 18)) }
                                        case 1000 { result := mul(SCALE, sub(3, 18)) }
                                        case 10000 { result := mul(SCALE, sub(4, 18)) }
                                        case 100000 { result := mul(SCALE, sub(5, 18)) }
                                        case 1000000 { result := mul(SCALE, sub(6, 18)) }
                                        case 10000000 { result := mul(SCALE, sub(7, 18)) }
                                        case 100000000 { result := mul(SCALE, sub(8, 18)) }
                                        case 1000000000 { result := mul(SCALE, sub(9, 18)) }
                                        case 10000000000 { result := mul(SCALE, sub(10, 18)) }
                                        case 100000000000 { result := mul(SCALE, sub(11, 18)) }
                                        case 1000000000000 { result := mul(SCALE, sub(12, 18)) }
                                        case 10000000000000 { result := mul(SCALE, sub(13, 18)) }
                                        case 100000000000000 { result := mul(SCALE, sub(14, 18)) }
                                        case 1000000000000000 { result := mul(SCALE, sub(15, 18)) }
                                        case 10000000000000000 { result := mul(SCALE, sub(16, 18)) }
                                        case 100000000000000000 { result := mul(SCALE, sub(17, 18)) }
                                        case 1000000000000000000 { result := 0 }
                                        case 10000000000000000000 { result := SCALE }
                                        case 100000000000000000000 { result := mul(SCALE, 2) }
                                        case 1000000000000000000000 { result := mul(SCALE, 3) }
                                        case 10000000000000000000000 { result := mul(SCALE, 4) }
                                        case 100000000000000000000000 { result := mul(SCALE, 5) }
                                        case 1000000000000000000000000 { result := mul(SCALE, 6) }
                                        case 10000000000000000000000000 { result := mul(SCALE, 7) }
                                        case 100000000000000000000000000 { result := mul(SCALE, 8) }
                                        case 1000000000000000000000000000 { result := mul(SCALE, 9) }
                                        case 10000000000000000000000000000 { result := mul(SCALE, 10) }
                                        case 100000000000000000000000000000 { result := mul(SCALE, 11) }
                                        case 1000000000000000000000000000000 { result := mul(SCALE, 12) }
                                        case 10000000000000000000000000000000 { result := mul(SCALE, 13) }
                                        case 100000000000000000000000000000000 { result := mul(SCALE, 14) }
                                        case 1000000000000000000000000000000000 { result := mul(SCALE, 15) }
                                        case 10000000000000000000000000000000000 { result := mul(SCALE, 16) }
                                        case 100000000000000000000000000000000000 { result := mul(SCALE, 17) }
                                        case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) }
                                        case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) }
                                        case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) }
                                        case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) }
                                        case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) }
                                        case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) }
                                        case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) }
                                        case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) }
                                        case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) }
                                        case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) }
                                        case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) }
                                        case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) }
                                        case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) }
                                        case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) }
                                        case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) }
                                        case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) }
                                        case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) }
                                        case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) }
                                        case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) }
                                        case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) }
                                        case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) }
                                        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) }
                                        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) }
                                        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) }
                                        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) }
                                        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) }
                                        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) }
                                        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) }
                                        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) }
                                        case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 59) }
                                        default {
                                            result := MAX_UD60x18
                                        }
                                    }
                                    if (result == MAX_UD60x18) {
                                        // Do the fixed-point division inline to save gas. The denominator is log2(10).
                                        unchecked {
                                            result = (log2(x) * SCALE) / 3_321928094887362347;
                                        }
                                    }
                                }
                                /// @notice Calculates the binary logarithm of x.
                                ///
                                /// @dev Based on the iterative approximation algorithm.
                                /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
                                ///
                                /// Requirements:
                                /// - x must be greater than or equal to SCALE, otherwise the result would be negative.
                                ///
                                /// Caveats:
                                /// - The results are nor perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
                                ///
                                /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the binary logarithm.
                                /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number.
                                function log2(uint256 x) internal pure returns (uint256 result) {
                                    if (x < SCALE) {
                                        revert PRBMathUD60x18__LogInputTooSmall(x);
                                    }
                                    unchecked {
                                        // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
                                        uint256 n = PRBMath.mostSignificantBit(x / SCALE);
                                        // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number. The operation can't overflow
                                        // because n is maximum 255 and SCALE is 1e18.
                                        result = n * SCALE;
                                        // This is y = x * 2^(-n).
                                        uint256 y = x >> n;
                                        // If y = 1, the fractional part is zero.
                                        if (y == SCALE) {
                                            return result;
                                        }
                                        // Calculate the fractional part via the iterative approximation.
                                        // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
                                        for (uint256 delta = HALF_SCALE; delta > 0; delta >>= 1) {
                                            y = (y * y) / SCALE;
                                            // Is y^2 > 2 and so in the range [2,4)?
                                            if (y >= 2 * SCALE) {
                                                // Add the 2^(-m) factor to the logarithm.
                                                result += delta;
                                                // Corresponds to z/2 on Wikipedia.
                                                y >>= 1;
                                            }
                                        }
                                    }
                                }
                                /// @notice Multiplies two unsigned 60.18-decimal fixed-point numbers together, returning a new unsigned 60.18-decimal
                                /// fixed-point number.
                                /// @dev See the documentation for the "PRBMath.mulDivFixedPoint" function.
                                /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
                                /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
                                /// @return result The product as an unsigned 60.18-decimal fixed-point number.
                                function mul(uint256 x, uint256 y) internal pure returns (uint256 result) {
                                    result = PRBMath.mulDivFixedPoint(x, y);
                                }
                                /// @notice Returns PI as an unsigned 60.18-decimal fixed-point number.
                                function pi() internal pure returns (uint256 result) {
                                    result = 3_141592653589793238;
                                }
                                /// @notice Raises x to the power of y.
                                ///
                                /// @dev Based on the insight that x^y = 2^(log2(x) * y).
                                ///
                                /// Requirements:
                                /// - All from "exp2", "log2" and "mul".
                                ///
                                /// Caveats:
                                /// - All from "exp2", "log2" and "mul".
                                /// - Assumes 0^0 is 1.
                                ///
                                /// @param x Number to raise to given power y, as an unsigned 60.18-decimal fixed-point number.
                                /// @param y Exponent to raise x to, as an unsigned 60.18-decimal fixed-point number.
                                /// @return result x raised to power y, as an unsigned 60.18-decimal fixed-point number.
                                function pow(uint256 x, uint256 y) internal pure returns (uint256 result) {
                                    if (x == 0) {
                                        result = y == 0 ? SCALE : uint256(0);
                                    } else {
                                        result = exp2(mul(log2(x), y));
                                    }
                                }
                                /// @notice Raises x (unsigned 60.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
                                /// famous algorithm "exponentiation by squaring".
                                ///
                                /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
                                ///
                                /// Requirements:
                                /// - The result must fit within MAX_UD60x18.
                                ///
                                /// Caveats:
                                /// - All from "mul".
                                /// - Assumes 0^0 is 1.
                                ///
                                /// @param x The base as an unsigned 60.18-decimal fixed-point number.
                                /// @param y The exponent as an uint256.
                                /// @return result The result as an unsigned 60.18-decimal fixed-point number.
                                function powu(uint256 x, uint256 y) internal pure returns (uint256 result) {
                                    // Calculate the first iteration of the loop in advance.
                                    result = y & 1 > 0 ? x : SCALE;
                                    // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
                                    for (y >>= 1; y > 0; y >>= 1) {
                                        x = PRBMath.mulDivFixedPoint(x, x);
                                        // Equivalent to "y % 2 == 1" but faster.
                                        if (y & 1 > 0) {
                                            result = PRBMath.mulDivFixedPoint(result, x);
                                        }
                                    }
                                }
                                /// @notice Returns 1 as an unsigned 60.18-decimal fixed-point number.
                                function scale() internal pure returns (uint256 result) {
                                    result = SCALE;
                                }
                                /// @notice Calculates the square root of x, rounding down.
                                /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
                                ///
                                /// Requirements:
                                /// - x must be less than MAX_UD60x18 / SCALE.
                                ///
                                /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the square root.
                                /// @return result The result as an unsigned 60.18-decimal fixed-point .
                                function sqrt(uint256 x) internal pure returns (uint256 result) {
                                    unchecked {
                                        if (x > MAX_UD60x18 / SCALE) {
                                            revert PRBMathUD60x18__SqrtOverflow(x);
                                        }
                                        // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two unsigned
                                        // 60.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root).
                                        result = PRBMath.sqrt(x * SCALE);
                                    }
                                }
                                /// @notice Converts a unsigned 60.18-decimal fixed-point number to basic integer form, rounding down in the process.
                                /// @param x The unsigned 60.18-decimal fixed-point number to convert.
                                /// @return result The same number in basic integer form.
                                function toUint(uint256 x) internal pure returns (uint256 result) {
                                    unchecked {
                                        result = x / SCALE;
                                    }
                                }
                            }
                            // SPDX-License-Identifier: MIT
                            pragma solidity 0.8.9;
                            interface IInterestRateModel {
                                function getBorrowRate(
                                    uint256 balance,
                                    uint256 totalBorrows,
                                    uint256 totalReserves
                                ) external view returns (uint256);
                                function utilizationRate(
                                    uint256 balance,
                                    uint256 borrows,
                                    uint256 reserves
                                ) external pure returns (uint256);
                                function getSupplyRate(
                                    uint256 balance,
                                    uint256 borrows,
                                    uint256 reserves,
                                    uint256 reserveFactor
                                ) external view returns (uint256);
                            }