ETH Price: $2,542.06 (+0.11%)

Transaction Decoder

Block:
16400711 at Jan-13-2023 09:58:35 PM +UTC
Transaction Fee:
0.002372052087727728 ETH $6.03
Gas Used:
72,922 Gas / 32.528620824 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x343715FA...1CA860e78
(Compound: Deployer 5)
0.900538724068541917 Eth
Nonce: 27
0.898166671980814189 Eth
Nonce: 28
0.002372052087727728
(builder0x69)
2.167550383633253459 Eth2.167576132057179011 Eth0.000025748423925552
0xA17581A9...6e593aE94

Execution Trace

TransparentUpgradeableProxy.CALL( )
  • Comet.DELEGATECALL( )
    File 1 of 2: TransparentUpgradeableProxy
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)
    pragma solidity ^0.8.0;
    import "../ERC1967/ERC1967Proxy.sol";
    /**
     * @dev This contract implements a proxy that is upgradeable by an admin.
     *
     * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
     * clashing], which can potentially be used in an attack, this contract uses the
     * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
     * things that go hand in hand:
     *
     * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
     * that call matches one of the admin functions exposed by the proxy itself.
     * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
     * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
     * "admin cannot fallback to proxy target".
     *
     * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
     * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
     * to sudden errors when trying to call a function from the proxy implementation.
     *
     * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
     * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
     */
    contract TransparentUpgradeableProxy is ERC1967Proxy {
        /**
         * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
         * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
         */
        constructor(
            address _logic,
            address admin_,
            bytes memory _data
        ) payable ERC1967Proxy(_logic, _data) {
            assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
            _changeAdmin(admin_);
        }
        /**
         * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
         */
        modifier ifAdmin() {
            if (msg.sender == _getAdmin()) {
                _;
            } else {
                _fallback();
            }
        }
        /**
         * @dev Returns the current admin.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
         *
         * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
         * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
         * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
         */
        function admin() external ifAdmin returns (address admin_) {
            admin_ = _getAdmin();
        }
        /**
         * @dev Returns the current implementation.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
         *
         * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
         * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
         * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
         */
        function implementation() external ifAdmin returns (address implementation_) {
            implementation_ = _implementation();
        }
        /**
         * @dev Changes the admin of the proxy.
         *
         * Emits an {AdminChanged} event.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
         */
        function changeAdmin(address newAdmin) external virtual ifAdmin {
            _changeAdmin(newAdmin);
        }
        /**
         * @dev Upgrade the implementation of the proxy.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
            _upgradeToAndCall(newImplementation, bytes(""), false);
        }
        /**
         * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
         * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
         * proxied contract.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
         */
        function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
            _upgradeToAndCall(newImplementation, data, true);
        }
        /**
         * @dev Returns the current admin.
         */
        function _admin() internal view virtual returns (address) {
            return _getAdmin();
        }
        /**
         * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
         */
        function _beforeFallback() internal virtual override {
            require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
            super._beforeFallback();
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)
    pragma solidity ^0.8.0;
    import "../Proxy.sol";
    import "./ERC1967Upgrade.sol";
    /**
     * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
     * implementation address that can be changed. This address is stored in storage in the location specified by
     * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
     * implementation behind the proxy.
     */
    contract ERC1967Proxy is Proxy, ERC1967Upgrade {
        /**
         * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
         *
         * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
         * function call, and allows initializating the storage of the proxy like a Solidity constructor.
         */
        constructor(address _logic, bytes memory _data) payable {
            assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
            _upgradeToAndCall(_logic, _data, false);
        }
        /**
         * @dev Returns the current implementation address.
         */
        function _implementation() internal view virtual override returns (address impl) {
            return ERC1967Upgrade._getImplementation();
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.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 overriden 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 internall 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 overriden 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 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.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.5.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
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/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) {
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
         */
        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
         */
        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
         */
        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
            assembly {
                r.slot := slot
            }
        }
    }
    

    File 2 of 2: Comet
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.15;
    import "./CometMainInterface.sol";
    import "./ERC20.sol";
    import "./IPriceFeed.sol";
    /**
     * @title Compound's Comet Contract
     * @notice An efficient monolithic money market protocol
     * @author Compound
     */
    contract Comet is CometMainInterface {
        /** General configuration constants **/
        /// @notice The admin of the protocol
        address public override immutable governor;
        /// @notice The account which may trigger pauses
        address public override immutable pauseGuardian;
        /// @notice The address of the base token contract
        address public override immutable baseToken;
        /// @notice The address of the price feed for the base token
        address public override immutable baseTokenPriceFeed;
        /// @notice The address of the extension contract delegate
        address public override immutable extensionDelegate;
        /// @notice The point in the supply rates separating the low interest rate slope and the high interest rate slope (factor)
        /// @dev uint64
        uint public override immutable supplyKink;
        /// @notice Per second supply interest rate slope applied when utilization is below kink (factor)
        /// @dev uint64
        uint public override immutable supplyPerSecondInterestRateSlopeLow;
        /// @notice Per second supply interest rate slope applied when utilization is above kink (factor)
        /// @dev uint64
        uint public override immutable supplyPerSecondInterestRateSlopeHigh;
        /// @notice Per second supply base interest rate (factor)
        /// @dev uint64
        uint public override immutable supplyPerSecondInterestRateBase;
        /// @notice The point in the borrow rate separating the low interest rate slope and the high interest rate slope (factor)
        /// @dev uint64
        uint public override immutable borrowKink;
        /// @notice Per second borrow interest rate slope applied when utilization is below kink (factor)
        /// @dev uint64
        uint public override immutable borrowPerSecondInterestRateSlopeLow;
        /// @notice Per second borrow interest rate slope applied when utilization is above kink (factor)
        /// @dev uint64
        uint public override immutable borrowPerSecondInterestRateSlopeHigh;
        /// @notice Per second borrow base interest rate (factor)
        /// @dev uint64
        uint public override immutable borrowPerSecondInterestRateBase;
        /// @notice The fraction of the liquidation penalty that goes to buyers of collateral instead of the protocol
        /// @dev uint64
        uint public override immutable storeFrontPriceFactor;
        /// @notice The scale for base token (must be less than 18 decimals)
        /// @dev uint64
        uint public override immutable baseScale;
        /// @notice The scale for reward tracking
        /// @dev uint64
        uint public override immutable trackingIndexScale;
        /// @notice The speed at which supply rewards are tracked (in trackingIndexScale)
        /// @dev uint64
        uint public override immutable baseTrackingSupplySpeed;
        /// @notice The speed at which borrow rewards are tracked (in trackingIndexScale)
        /// @dev uint64
        uint public override immutable baseTrackingBorrowSpeed;
        /// @notice The minimum amount of base principal wei for rewards to accrue
        /// @dev This must be large enough so as to prevent division by base wei from overflowing the 64 bit indices
        /// @dev uint104
        uint public override immutable baseMinForRewards;
        /// @notice The minimum base amount required to initiate a borrow
        uint public override immutable baseBorrowMin;
        /// @notice The minimum base token reserves which must be held before collateral is hodled
        uint public override immutable targetReserves;
        /// @notice The number of decimals for wrapped base token
        uint8 public override immutable decimals;
        /// @notice The number of assets this contract actually supports
        uint8 public override immutable numAssets;
        /// @notice Factor to divide by when accruing rewards in order to preserve 6 decimals (i.e. baseScale / 1e6)
        uint internal immutable accrualDescaleFactor;
        /** Collateral asset configuration (packed) **/
        uint256 internal immutable asset00_a;
        uint256 internal immutable asset00_b;
        uint256 internal immutable asset01_a;
        uint256 internal immutable asset01_b;
        uint256 internal immutable asset02_a;
        uint256 internal immutable asset02_b;
        uint256 internal immutable asset03_a;
        uint256 internal immutable asset03_b;
        uint256 internal immutable asset04_a;
        uint256 internal immutable asset04_b;
        uint256 internal immutable asset05_a;
        uint256 internal immutable asset05_b;
        uint256 internal immutable asset06_a;
        uint256 internal immutable asset06_b;
        uint256 internal immutable asset07_a;
        uint256 internal immutable asset07_b;
        uint256 internal immutable asset08_a;
        uint256 internal immutable asset08_b;
        uint256 internal immutable asset09_a;
        uint256 internal immutable asset09_b;
        uint256 internal immutable asset10_a;
        uint256 internal immutable asset10_b;
        uint256 internal immutable asset11_a;
        uint256 internal immutable asset11_b;
        uint256 internal immutable asset12_a;
        uint256 internal immutable asset12_b;
        uint256 internal immutable asset13_a;
        uint256 internal immutable asset13_b;
        uint256 internal immutable asset14_a;
        uint256 internal immutable asset14_b;
        /**
         * @notice Construct a new protocol instance
         * @param config The mapping of initial/constant parameters
         **/
        constructor(Configuration memory config) {
            // Sanity checks
            uint8 decimals_ = ERC20(config.baseToken).decimals();
            if (decimals_ > MAX_BASE_DECIMALS) revert BadDecimals();
            if (config.storeFrontPriceFactor > FACTOR_SCALE) revert BadDiscount();
            if (config.assetConfigs.length > MAX_ASSETS) revert TooManyAssets();
            if (config.baseMinForRewards == 0) revert BadMinimum();
            if (IPriceFeed(config.baseTokenPriceFeed).decimals() != PRICE_FEED_DECIMALS) revert BadDecimals();
            // Copy configuration
            unchecked {
                governor = config.governor;
                pauseGuardian = config.pauseGuardian;
                baseToken = config.baseToken;
                baseTokenPriceFeed = config.baseTokenPriceFeed;
                extensionDelegate = config.extensionDelegate;
                storeFrontPriceFactor = config.storeFrontPriceFactor;
                decimals = decimals_;
                baseScale = uint64(10 ** decimals_);
                trackingIndexScale = config.trackingIndexScale;
                if (baseScale < BASE_ACCRUAL_SCALE) revert BadDecimals();
                accrualDescaleFactor = baseScale / BASE_ACCRUAL_SCALE;
                baseMinForRewards = config.baseMinForRewards;
                baseTrackingSupplySpeed = config.baseTrackingSupplySpeed;
                baseTrackingBorrowSpeed = config.baseTrackingBorrowSpeed;
                baseBorrowMin = config.baseBorrowMin;
                targetReserves = config.targetReserves;
            }
            // Set interest rate model configs
            unchecked {
                supplyKink = config.supplyKink;
                supplyPerSecondInterestRateSlopeLow = config.supplyPerYearInterestRateSlopeLow / SECONDS_PER_YEAR;
                supplyPerSecondInterestRateSlopeHigh = config.supplyPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR;
                supplyPerSecondInterestRateBase = config.supplyPerYearInterestRateBase / SECONDS_PER_YEAR;
                borrowKink = config.borrowKink;
                borrowPerSecondInterestRateSlopeLow = config.borrowPerYearInterestRateSlopeLow / SECONDS_PER_YEAR;
                borrowPerSecondInterestRateSlopeHigh = config.borrowPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR;
                borrowPerSecondInterestRateBase = config.borrowPerYearInterestRateBase / SECONDS_PER_YEAR;
            }
            // Set asset info
            numAssets = uint8(config.assetConfigs.length);
            (asset00_a, asset00_b) = getPackedAssetInternal(config.assetConfigs, 0);
            (asset01_a, asset01_b) = getPackedAssetInternal(config.assetConfigs, 1);
            (asset02_a, asset02_b) = getPackedAssetInternal(config.assetConfigs, 2);
            (asset03_a, asset03_b) = getPackedAssetInternal(config.assetConfigs, 3);
            (asset04_a, asset04_b) = getPackedAssetInternal(config.assetConfigs, 4);
            (asset05_a, asset05_b) = getPackedAssetInternal(config.assetConfigs, 5);
            (asset06_a, asset06_b) = getPackedAssetInternal(config.assetConfigs, 6);
            (asset07_a, asset07_b) = getPackedAssetInternal(config.assetConfigs, 7);
            (asset08_a, asset08_b) = getPackedAssetInternal(config.assetConfigs, 8);
            (asset09_a, asset09_b) = getPackedAssetInternal(config.assetConfigs, 9);
            (asset10_a, asset10_b) = getPackedAssetInternal(config.assetConfigs, 10);
            (asset11_a, asset11_b) = getPackedAssetInternal(config.assetConfigs, 11);
            (asset12_a, asset12_b) = getPackedAssetInternal(config.assetConfigs, 12);
            (asset13_a, asset13_b) = getPackedAssetInternal(config.assetConfigs, 13);
            (asset14_a, asset14_b) = getPackedAssetInternal(config.assetConfigs, 14);
        }
        /**
         * @notice Initialize storage for the contract
         * @dev Can be used from constructor or proxy
         */
        function initializeStorage() override external {
            if (lastAccrualTime != 0) revert AlreadyInitialized();
            // Initialize aggregates
            lastAccrualTime = getNowInternal();
            baseSupplyIndex = BASE_INDEX_SCALE;
            baseBorrowIndex = BASE_INDEX_SCALE;
            // Implicit initialization (not worth increasing contract size)
            // trackingSupplyIndex = 0;
            // trackingBorrowIndex = 0;
        }
        /**
         * @dev Checks and gets the packed asset info for storage
         */
        function getPackedAssetInternal(AssetConfig[] memory assetConfigs, uint i) internal view returns (uint256, uint256) {
            AssetConfig memory assetConfig;
            if (i < assetConfigs.length) {
                assembly {
                    assetConfig := mload(add(add(assetConfigs, 0x20), mul(i, 0x20)))
                }
            } else {
                return (0, 0);
            }
            address asset = assetConfig.asset;
            address priceFeed = assetConfig.priceFeed;
            uint8 decimals_ = assetConfig.decimals;
            // Short-circuit if asset is nil
            if (asset == address(0)) {
                return (0, 0);
            }
            // Sanity check price feed and asset decimals
            if (IPriceFeed(priceFeed).decimals() != PRICE_FEED_DECIMALS) revert BadDecimals();
            if (ERC20(asset).decimals() != decimals_) revert BadDecimals();
            // Ensure collateral factors are within range
            if (assetConfig.borrowCollateralFactor >= assetConfig.liquidateCollateralFactor) revert BorrowCFTooLarge();
            if (assetConfig.liquidateCollateralFactor > MAX_COLLATERAL_FACTOR) revert LiquidateCFTooLarge();
            unchecked {
                // Keep 4 decimals for each factor
                uint64 descale = FACTOR_SCALE / 1e4;
                uint16 borrowCollateralFactor = uint16(assetConfig.borrowCollateralFactor / descale);
                uint16 liquidateCollateralFactor = uint16(assetConfig.liquidateCollateralFactor / descale);
                uint16 liquidationFactor = uint16(assetConfig.liquidationFactor / descale);
                // Be nice and check descaled values are still within range
                if (borrowCollateralFactor >= liquidateCollateralFactor) revert BorrowCFTooLarge();
                // Keep whole units of asset for supply cap
                uint64 supplyCap = uint64(assetConfig.supplyCap / (10 ** decimals_));
                uint256 word_a = (uint160(asset) << 0 |
                                  uint256(borrowCollateralFactor) << 160 |
                                  uint256(liquidateCollateralFactor) << 176 |
                                  uint256(liquidationFactor) << 192);
                uint256 word_b = (uint160(priceFeed) << 0 |
                                  uint256(decimals_) << 160 |
                                  uint256(supplyCap) << 168);
                return (word_a, word_b);
            }
        }
        /**
         * @notice Get the i-th asset info, according to the order they were passed in originally
         * @param i The index of the asset info to get
         * @return The asset info object
         */
        function getAssetInfo(uint8 i) override public view returns (AssetInfo memory) {
            if (i >= numAssets) revert BadAsset();
            uint256 word_a;
            uint256 word_b;
            if (i == 0) {
                word_a = asset00_a;
                word_b = asset00_b;
            } else if (i == 1) {
                word_a = asset01_a;
                word_b = asset01_b;
            } else if (i == 2) {
                word_a = asset02_a;
                word_b = asset02_b;
            } else if (i == 3) {
                word_a = asset03_a;
                word_b = asset03_b;
            } else if (i == 4) {
                word_a = asset04_a;
                word_b = asset04_b;
            } else if (i == 5) {
                word_a = asset05_a;
                word_b = asset05_b;
            } else if (i == 6) {
                word_a = asset06_a;
                word_b = asset06_b;
            } else if (i == 7) {
                word_a = asset07_a;
                word_b = asset07_b;
            } else if (i == 8) {
                word_a = asset08_a;
                word_b = asset08_b;
            } else if (i == 9) {
                word_a = asset09_a;
                word_b = asset09_b;
            } else if (i == 10) {
                word_a = asset10_a;
                word_b = asset10_b;
            } else if (i == 11) {
                word_a = asset11_a;
                word_b = asset11_b;
            } else if (i == 12) {
                word_a = asset12_a;
                word_b = asset12_b;
            } else if (i == 13) {
                word_a = asset13_a;
                word_b = asset13_b;
            } else if (i == 14) {
                word_a = asset14_a;
                word_b = asset14_b;
            } else {
                revert Absurd();
            }
            address asset = address(uint160(word_a & type(uint160).max));
            uint64 rescale = FACTOR_SCALE / 1e4;
            uint64 borrowCollateralFactor = uint64(((word_a >> 160) & type(uint16).max) * rescale);
            uint64 liquidateCollateralFactor = uint64(((word_a >> 176) & type(uint16).max) * rescale);
            uint64 liquidationFactor = uint64(((word_a >> 192) & type(uint16).max) * rescale);
            address priceFeed = address(uint160(word_b & type(uint160).max));
            uint8 decimals_ = uint8(((word_b >> 160) & type(uint8).max));
            uint64 scale = uint64(10 ** decimals_);
            uint128 supplyCap = uint128(((word_b >> 168) & type(uint64).max) * scale);
            return AssetInfo({
                offset: i,
                asset: asset,
                priceFeed: priceFeed,
                scale: scale,
                borrowCollateralFactor: borrowCollateralFactor,
                liquidateCollateralFactor: liquidateCollateralFactor,
                liquidationFactor: liquidationFactor,
                supplyCap: supplyCap
             });
        }
        /**
         * @dev Determine index of asset that matches given address
         */
        function getAssetInfoByAddress(address asset) override public view returns (AssetInfo memory) {
            for (uint8 i = 0; i < numAssets; ) {
                AssetInfo memory assetInfo = getAssetInfo(i);
                if (assetInfo.asset == asset) {
                    return assetInfo;
                }
                unchecked { i++; }
            }
            revert BadAsset();
        }
        /**
         * @return The current timestamp
         **/
        function getNowInternal() virtual internal view returns (uint40) {
            if (block.timestamp >= 2**40) revert TimestampTooLarge();
            return uint40(block.timestamp);
        }
        /**
         * @dev Calculate accrued interest indices for base token supply and borrows
         **/
        function accruedInterestIndices(uint timeElapsed) internal view returns (uint64, uint64) {
            uint64 baseSupplyIndex_ = baseSupplyIndex;
            uint64 baseBorrowIndex_ = baseBorrowIndex;
            if (timeElapsed > 0) {
                uint utilization = getUtilization();
                uint supplyRate = getSupplyRate(utilization);
                uint borrowRate = getBorrowRate(utilization);
                baseSupplyIndex_ += safe64(mulFactor(baseSupplyIndex_, supplyRate * timeElapsed));
                baseBorrowIndex_ += safe64(mulFactor(baseBorrowIndex_, borrowRate * timeElapsed));
            }
            return (baseSupplyIndex_, baseBorrowIndex_);
        }
        /**
         * @dev Accrue interest (and rewards) in base token supply and borrows
         **/
        function accrueInternal() internal {
            uint40 now_ = getNowInternal();
            uint timeElapsed = uint256(now_ - lastAccrualTime);
            if (timeElapsed > 0) {
                (baseSupplyIndex, baseBorrowIndex) = accruedInterestIndices(timeElapsed);
                if (totalSupplyBase >= baseMinForRewards) {
                    trackingSupplyIndex += safe64(divBaseWei(baseTrackingSupplySpeed * timeElapsed, totalSupplyBase));
                }
                if (totalBorrowBase >= baseMinForRewards) {
                    trackingBorrowIndex += safe64(divBaseWei(baseTrackingBorrowSpeed * timeElapsed, totalBorrowBase));
                }
                lastAccrualTime = now_;
            }
        }
        /**
         * @notice Accrue interest and rewards for an account
         **/
        function accrueAccount(address account) override external {
            accrueInternal();
            UserBasic memory basic = userBasic[account];
            updateBasePrincipal(account, basic, basic.principal);
        }
        /**
         * @dev Note: Does not accrue interest first
         * @param utilization The utilization to check the supply rate for
         * @return The per second supply rate at `utilization`
         */
        function getSupplyRate(uint utilization) override public view returns (uint64) {
            if (utilization <= supplyKink) {
                // interestRateBase + interestRateSlopeLow * utilization
                return safe64(supplyPerSecondInterestRateBase + mulFactor(supplyPerSecondInterestRateSlopeLow, utilization));
            } else {
                // interestRateBase + interestRateSlopeLow * kink + interestRateSlopeHigh * (utilization - kink)
                return safe64(supplyPerSecondInterestRateBase + mulFactor(supplyPerSecondInterestRateSlopeLow, supplyKink) + mulFactor(supplyPerSecondInterestRateSlopeHigh, (utilization - supplyKink)));
            }
        }
        /**
         * @dev Note: Does not accrue interest first
         * @param utilization The utilization to check the borrow rate for
         * @return The per second borrow rate at `utilization`
         */
        function getBorrowRate(uint utilization) override public view returns (uint64) {
            if (utilization <= borrowKink) {
                // interestRateBase + interestRateSlopeLow * utilization
                return safe64(borrowPerSecondInterestRateBase + mulFactor(borrowPerSecondInterestRateSlopeLow, utilization));
            } else {
                // interestRateBase + interestRateSlopeLow * kink + interestRateSlopeHigh * (utilization - kink)
                return safe64(borrowPerSecondInterestRateBase + mulFactor(borrowPerSecondInterestRateSlopeLow, borrowKink) + mulFactor(borrowPerSecondInterestRateSlopeHigh, (utilization - borrowKink)));
            }
        }
        /**
         * @dev Note: Does not accrue interest first
         * @return The utilization rate of the base asset
         */
        function getUtilization() override public view returns (uint) {
            uint totalSupply_ = presentValueSupply(baseSupplyIndex, totalSupplyBase);
            uint totalBorrow_ = presentValueBorrow(baseBorrowIndex, totalBorrowBase);
            if (totalSupply_ == 0) {
                return 0;
            } else {
                return totalBorrow_ * FACTOR_SCALE / totalSupply_;
            }
        }
        /**
         * @notice Get the current price from a feed
         * @param priceFeed The address of a price feed
         * @return The price, scaled by `PRICE_SCALE`
         */
        function getPrice(address priceFeed) override public view returns (uint256) {
            (, int price, , , ) = IPriceFeed(priceFeed).latestRoundData();
            if (price <= 0) revert BadPrice();
            return uint256(price);
        }
        /**
         * @notice Gets the total balance of protocol collateral reserves for an asset
         * @dev Note: Reverts if collateral reserves are somehow negative, which should not be possible
         * @param asset The collateral asset
         */
        function getCollateralReserves(address asset) override public view returns (uint) {
            return ERC20(asset).balanceOf(address(this)) - totalsCollateral[asset].totalSupplyAsset;
        }
        /**
         * @notice Gets the total amount of protocol reserves of the base asset
         */
        function getReserves() override public view returns (int) {
            (uint64 baseSupplyIndex_, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
            uint balance = ERC20(baseToken).balanceOf(address(this));
            uint totalSupply_ = presentValueSupply(baseSupplyIndex_, totalSupplyBase);
            uint totalBorrow_ = presentValueBorrow(baseBorrowIndex_, totalBorrowBase);
            return signed256(balance) - signed256(totalSupply_) + signed256(totalBorrow_);
        }
        /**
         * @notice Check whether an account has enough collateral to borrow
         * @param account The address to check
         * @return Whether the account is minimally collateralized enough to borrow
         */
        function isBorrowCollateralized(address account) override public view returns (bool) {
            int104 principal = userBasic[account].principal;
            if (principal >= 0) {
                return true;
            }
            uint16 assetsIn = userBasic[account].assetsIn;
            int liquidity = signedMulPrice(
                presentValue(principal),
                getPrice(baseTokenPriceFeed),
                uint64(baseScale)
            );
            for (uint8 i = 0; i < numAssets; ) {
                if (isInAsset(assetsIn, i)) {
                    if (liquidity >= 0) {
                        return true;
                    }
                    AssetInfo memory asset = getAssetInfo(i);
                    uint newAmount = mulPrice(
                        userCollateral[account][asset.asset].balance,
                        getPrice(asset.priceFeed),
                        asset.scale
                    );
                    liquidity += signed256(mulFactor(
                        newAmount,
                        asset.borrowCollateralFactor
                    ));
                }
                unchecked { i++; }
            }
            return liquidity >= 0;
        }
        /**
         * @notice Check whether an account has enough collateral to not be liquidated
         * @param account The address to check
         * @return Whether the account is minimally collateralized enough to not be liquidated
         */
        function isLiquidatable(address account) override public view returns (bool) {
            int104 principal = userBasic[account].principal;
            if (principal >= 0) {
                return false;
            }
            uint16 assetsIn = userBasic[account].assetsIn;
            int liquidity = signedMulPrice(
                presentValue(principal),
                getPrice(baseTokenPriceFeed),
                uint64(baseScale)
            );
            for (uint8 i = 0; i < numAssets; ) {
                if (isInAsset(assetsIn, i)) {
                    if (liquidity >= 0) {
                        return false;
                    }
                    AssetInfo memory asset = getAssetInfo(i);
                    uint newAmount = mulPrice(
                        userCollateral[account][asset.asset].balance,
                        getPrice(asset.priceFeed),
                        asset.scale
                    );
                    liquidity += signed256(mulFactor(
                        newAmount,
                        asset.liquidateCollateralFactor
                    ));
                }
                unchecked { i++; }
            }
            return liquidity < 0;
        }
        /**
         * @dev The change in principal broken into repay and supply amounts
         */
        function repayAndSupplyAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) {
            // If the new principal is less than the old principal, then no amount has been repaid or supplied
            if (newPrincipal < oldPrincipal) return (0, 0);
            if (newPrincipal <= 0) {
                return (uint104(newPrincipal - oldPrincipal), 0);
            } else if (oldPrincipal >= 0) {
                return (0, uint104(newPrincipal - oldPrincipal));
            } else {
                return (uint104(-oldPrincipal), uint104(newPrincipal));
            }
        }
        /**
         * @dev The change in principal broken into withdraw and borrow amounts
         */
        function withdrawAndBorrowAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) {
            // If the new principal is greater than the old principal, then no amount has been withdrawn or borrowed
            if (newPrincipal > oldPrincipal) return (0, 0);
            if (newPrincipal >= 0) {
                return (uint104(oldPrincipal - newPrincipal), 0);
            } else if (oldPrincipal <= 0) {
                return (0, uint104(oldPrincipal - newPrincipal));
            } else {
                return (uint104(oldPrincipal), uint104(-newPrincipal));
            }
        }
        /**
         * @notice Pauses different actions within Comet
         * @param supplyPaused Boolean for pausing supply actions
         * @param transferPaused Boolean for pausing transfer actions
         * @param withdrawPaused Boolean for pausing withdraw actions
         * @param absorbPaused Boolean for pausing absorb actions
         * @param buyPaused Boolean for pausing buy actions
         */
        function pause(
            bool supplyPaused,
            bool transferPaused,
            bool withdrawPaused,
            bool absorbPaused,
            bool buyPaused
        ) override external {
            if (msg.sender != governor && msg.sender != pauseGuardian) revert Unauthorized();
            pauseFlags =
                uint8(0) |
                (toUInt8(supplyPaused) << PAUSE_SUPPLY_OFFSET) |
                (toUInt8(transferPaused) << PAUSE_TRANSFER_OFFSET) |
                (toUInt8(withdrawPaused) << PAUSE_WITHDRAW_OFFSET) |
                (toUInt8(absorbPaused) << PAUSE_ABSORB_OFFSET) |
                (toUInt8(buyPaused) << PAUSE_BUY_OFFSET);
            emit PauseAction(supplyPaused, transferPaused, withdrawPaused, absorbPaused, buyPaused);
        }
        /**
         * @return Whether or not supply actions are paused
         */
        function isSupplyPaused() override public view returns (bool) {
            return toBool(pauseFlags & (uint8(1) << PAUSE_SUPPLY_OFFSET));
        }
        /**
         * @return Whether or not transfer actions are paused
         */
        function isTransferPaused() override public view returns (bool) {
            return toBool(pauseFlags & (uint8(1) << PAUSE_TRANSFER_OFFSET));
        }
        /**
         * @return Whether or not withdraw actions are paused
         */
        function isWithdrawPaused() override public view returns (bool) {
            return toBool(pauseFlags & (uint8(1) << PAUSE_WITHDRAW_OFFSET));
        }
        /**
         * @return Whether or not absorb actions are paused
         */
        function isAbsorbPaused() override public view returns (bool) {
            return toBool(pauseFlags & (uint8(1) << PAUSE_ABSORB_OFFSET));
        }
        /**
         * @return Whether or not buy actions are paused
         */
        function isBuyPaused() override public view returns (bool) {
            return toBool(pauseFlags & (uint8(1) << PAUSE_BUY_OFFSET));
        }
        /**
         * @dev Multiply a number by a factor
         */
        function mulFactor(uint n, uint factor) internal pure returns (uint) {
            return n * factor / FACTOR_SCALE;
        }
        /**
         * @dev Divide a number by an amount of base
         */
        function divBaseWei(uint n, uint baseWei) internal view returns (uint) {
            return n * baseScale / baseWei;
        }
        /**
         * @dev Multiply a `fromScale` quantity by a price, returning a common price quantity
         */
        function mulPrice(uint n, uint price, uint64 fromScale) internal pure returns (uint) {
            return n * price / fromScale;
        }
        /**
         * @dev Multiply a signed `fromScale` quantity by a price, returning a common price quantity
         */
        function signedMulPrice(int n, uint price, uint64 fromScale) internal pure returns (int) {
            return n * signed256(price) / int256(uint256(fromScale));
        }
        /**
         * @dev Divide a common price quantity by a price, returning a `toScale` quantity
         */
        function divPrice(uint n, uint price, uint64 toScale) internal pure returns (uint) {
            return n * toScale / price;
        }
        /**
         * @dev Whether user has a non-zero balance of an asset, given assetsIn flags
         */
        function isInAsset(uint16 assetsIn, uint8 assetOffset) internal pure returns (bool) {
            return (assetsIn & (uint16(1) << assetOffset) != 0);
        }
        /**
         * @dev Update assetsIn bit vector if user has entered or exited an asset
         */
        function updateAssetsIn(
            address account,
            AssetInfo memory assetInfo,
            uint128 initialUserBalance,
            uint128 finalUserBalance
        ) internal {
            if (initialUserBalance == 0 && finalUserBalance != 0) {
                // set bit for asset
                userBasic[account].assetsIn |= (uint16(1) << assetInfo.offset);
            } else if (initialUserBalance != 0 && finalUserBalance == 0) {
                // clear bit for asset
                userBasic[account].assetsIn &= ~(uint16(1) << assetInfo.offset);
            }
        }
        /**
         * @dev Write updated principal to store and tracking participation
         */
        function updateBasePrincipal(address account, UserBasic memory basic, int104 principalNew) internal {
            int104 principal = basic.principal;
            basic.principal = principalNew;
            if (principal >= 0) {
                uint indexDelta = uint256(trackingSupplyIndex - basic.baseTrackingIndex);
                basic.baseTrackingAccrued += safe64(uint104(principal) * indexDelta / trackingIndexScale / accrualDescaleFactor);
            } else {
                uint indexDelta = uint256(trackingBorrowIndex - basic.baseTrackingIndex);
                basic.baseTrackingAccrued += safe64(uint104(-principal) * indexDelta / trackingIndexScale / accrualDescaleFactor);
            }
            if (principalNew >= 0) {
                basic.baseTrackingIndex = trackingSupplyIndex;
            } else {
                basic.baseTrackingIndex = trackingBorrowIndex;
            }
            userBasic[account] = basic;
        }
        /**
         * @dev Safe ERC20 transfer in, assumes no fee is charged and amount is transferred
         */
        function doTransferIn(address asset, address from, uint amount) internal {
            bool success = ERC20(asset).transferFrom(from, address(this), amount);
            if (!success) revert TransferInFailed();
        }
        /**
         * @dev Safe ERC20 transfer out
         */
        function doTransferOut(address asset, address to, uint amount) internal {
            bool success = ERC20(asset).transfer(to, amount);
            if (!success) revert TransferOutFailed();
        }
        /**
         * @notice Supply an amount of asset to the protocol
         * @param asset The asset to supply
         * @param amount The quantity to supply
         */
        function supply(address asset, uint amount) override external {
            return supplyInternal(msg.sender, msg.sender, msg.sender, asset, amount);
        }
        /**
         * @notice Supply an amount of asset to dst
         * @param dst The address which will hold the balance
         * @param asset The asset to supply
         * @param amount The quantity to supply
         */
        function supplyTo(address dst, address asset, uint amount) override external {
            return supplyInternal(msg.sender, msg.sender, dst, asset, amount);
        }
        /**
         * @notice Supply an amount of asset from `from` to dst, if allowed
         * @param from The supplier address
         * @param dst The address which will hold the balance
         * @param asset The asset to supply
         * @param amount The quantity to supply
         */
        function supplyFrom(address from, address dst, address asset, uint amount) override external {
            return supplyInternal(msg.sender, from, dst, asset, amount);
        }
        /**
         * @dev Supply either collateral or base asset, depending on the asset, if operator is allowed
         * @dev Note: Specifying an `amount` of uint256.max will repay all of `dst`'s accrued base borrow balance
         */
        function supplyInternal(address operator, address from, address dst, address asset, uint amount) internal {
            if (isSupplyPaused()) revert Paused();
            if (!hasPermission(from, operator)) revert Unauthorized();
            if (asset == baseToken) {
                if (amount == type(uint256).max) {
                    amount = borrowBalanceOf(dst);
                }
                return supplyBase(from, dst, amount);
            } else {
                return supplyCollateral(from, dst, asset, safe128(amount));
            }
        }
        /**
         * @dev Supply an amount of base asset from `from` to dst
         */
        function supplyBase(address from, address dst, uint256 amount) internal {
            doTransferIn(baseToken, from, amount);
            accrueInternal();
            UserBasic memory dstUser = userBasic[dst];
            int104 dstPrincipal = dstUser.principal;
            int256 dstBalance = presentValue(dstPrincipal) + signed256(amount);
            int104 dstPrincipalNew = principalValue(dstBalance);
            (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(dstPrincipal, dstPrincipalNew);
            totalSupplyBase += supplyAmount;
            totalBorrowBase -= repayAmount;
            updateBasePrincipal(dst, dstUser, dstPrincipalNew);
            emit Supply(from, dst, amount);
            if (supplyAmount > 0) {
                emit Transfer(address(0), dst, presentValueSupply(baseSupplyIndex, supplyAmount));
            }
        }
        /**
         * @dev Supply an amount of collateral asset from `from` to dst
         */
        function supplyCollateral(address from, address dst, address asset, uint128 amount) internal {
            doTransferIn(asset, from, amount);
            AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
            TotalsCollateral memory totals = totalsCollateral[asset];
            totals.totalSupplyAsset += amount;
            if (totals.totalSupplyAsset > assetInfo.supplyCap) revert SupplyCapExceeded();
            uint128 dstCollateral = userCollateral[dst][asset].balance;
            uint128 dstCollateralNew = dstCollateral + amount;
            totalsCollateral[asset] = totals;
            userCollateral[dst][asset].balance = dstCollateralNew;
            updateAssetsIn(dst, assetInfo, dstCollateral, dstCollateralNew);
            emit SupplyCollateral(from, dst, asset, amount);
        }
        /**
         * @notice ERC20 transfer an amount of base token to dst
         * @param dst The recipient address
         * @param amount The quantity to transfer
         * @return true
         */
        function transfer(address dst, uint amount) override external returns (bool) {
            transferInternal(msg.sender, msg.sender, dst, baseToken, amount);
            return true;
        }
        /**
         * @notice ERC20 transfer an amount of base token from src to dst, if allowed
         * @param src The sender address
         * @param dst The recipient address
         * @param amount The quantity to transfer
         * @return true
         */
        function transferFrom(address src, address dst, uint amount) override external returns (bool) {
            transferInternal(msg.sender, src, dst, baseToken, amount);
            return true;
        }
        /**
         * @notice Transfer an amount of asset to dst
         * @param dst The recipient address
         * @param asset The asset to transfer
         * @param amount The quantity to transfer
         */
        function transferAsset(address dst, address asset, uint amount) override external {
            return transferInternal(msg.sender, msg.sender, dst, asset, amount);
        }
        /**
         * @notice Transfer an amount of asset from src to dst, if allowed
         * @param src The sender address
         * @param dst The recipient address
         * @param asset The asset to transfer
         * @param amount The quantity to transfer
         */
        function transferAssetFrom(address src, address dst, address asset, uint amount) override external {
            return transferInternal(msg.sender, src, dst, asset, amount);
        }
        /**
         * @dev Transfer either collateral or base asset, depending on the asset, if operator is allowed
         * @dev Note: Specifying an `amount` of uint256.max will transfer all of `src`'s accrued base balance
         */
        function transferInternal(address operator, address src, address dst, address asset, uint amount) internal {
            if (isTransferPaused()) revert Paused();
            if (!hasPermission(src, operator)) revert Unauthorized();
            if (src == dst) revert NoSelfTransfer();
            if (asset == baseToken) {
                if (amount == type(uint256).max) {
                    amount = balanceOf(src);
                }
                return transferBase(src, dst, amount);
            } else {
                return transferCollateral(src, dst, asset, safe128(amount));
            }
        }
        /**
         * @dev Transfer an amount of base asset from src to dst, borrowing if possible/necessary
         */
        function transferBase(address src, address dst, uint256 amount) internal {
            accrueInternal();
            UserBasic memory srcUser = userBasic[src];
            UserBasic memory dstUser = userBasic[dst];
            int104 srcPrincipal = srcUser.principal;
            int104 dstPrincipal = dstUser.principal;
            int256 srcBalance = presentValue(srcPrincipal) - signed256(amount);
            int256 dstBalance = presentValue(dstPrincipal) + signed256(amount);
            int104 srcPrincipalNew = principalValue(srcBalance);
            int104 dstPrincipalNew = principalValue(dstBalance);
            (uint104 withdrawAmount, uint104 borrowAmount) = withdrawAndBorrowAmount(srcPrincipal, srcPrincipalNew);
            (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(dstPrincipal, dstPrincipalNew);
            // Note: Instead of `total += addAmount - subAmount` to avoid underflow errors.
            totalSupplyBase = totalSupplyBase + supplyAmount - withdrawAmount;
            totalBorrowBase = totalBorrowBase + borrowAmount - repayAmount;
            updateBasePrincipal(src, srcUser, srcPrincipalNew);
            updateBasePrincipal(dst, dstUser, dstPrincipalNew);
            if (srcBalance < 0) {
                if (uint256(-srcBalance) < baseBorrowMin) revert BorrowTooSmall();
                if (!isBorrowCollateralized(src)) revert NotCollateralized();
            }
            if (withdrawAmount > 0) {
                emit Transfer(src, address(0), presentValueSupply(baseSupplyIndex, withdrawAmount));
            }
            if (supplyAmount > 0) {
                emit Transfer(address(0), dst, presentValueSupply(baseSupplyIndex, supplyAmount));
            }
        }
        /**
         * @dev Transfer an amount of collateral asset from src to dst
         */
        function transferCollateral(address src, address dst, address asset, uint128 amount) internal {
            uint128 srcCollateral = userCollateral[src][asset].balance;
            uint128 dstCollateral = userCollateral[dst][asset].balance;
            uint128 srcCollateralNew = srcCollateral - amount;
            uint128 dstCollateralNew = dstCollateral + amount;
            userCollateral[src][asset].balance = srcCollateralNew;
            userCollateral[dst][asset].balance = dstCollateralNew;
            AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
            updateAssetsIn(src, assetInfo, srcCollateral, srcCollateralNew);
            updateAssetsIn(dst, assetInfo, dstCollateral, dstCollateralNew);
            // Note: no accrue interest, BorrowCF < LiquidationCF covers small changes
            if (!isBorrowCollateralized(src)) revert NotCollateralized();
            emit TransferCollateral(src, dst, asset, amount);
        }
        /**
         * @notice Withdraw an amount of asset from the protocol
         * @param asset The asset to withdraw
         * @param amount The quantity to withdraw
         */
        function withdraw(address asset, uint amount) override external {
            return withdrawInternal(msg.sender, msg.sender, msg.sender, asset, amount);
        }
        /**
         * @notice Withdraw an amount of asset to `to`
         * @param to The recipient address
         * @param asset The asset to withdraw
         * @param amount The quantity to withdraw
         */
        function withdrawTo(address to, address asset, uint amount) override external {
            return withdrawInternal(msg.sender, msg.sender, to, asset, amount);
        }
        /**
         * @notice Withdraw an amount of asset from src to `to`, if allowed
         * @param src The sender address
         * @param to The recipient address
         * @param asset The asset to withdraw
         * @param amount The quantity to withdraw
         */
        function withdrawFrom(address src, address to, address asset, uint amount) override external {
            return withdrawInternal(msg.sender, src, to, asset, amount);
        }
        /**
         * @dev Withdraw either collateral or base asset, depending on the asset, if operator is allowed
         * @dev Note: Specifying an `amount` of uint256.max will withdraw all of `src`'s accrued base balance
         */
        function withdrawInternal(address operator, address src, address to, address asset, uint amount) internal {
            if (isWithdrawPaused()) revert Paused();
            if (!hasPermission(src, operator)) revert Unauthorized();
            if (asset == baseToken) {
                if (amount == type(uint256).max) {
                    amount = balanceOf(src);
                }
                return withdrawBase(src, to, amount);
            } else {
                return withdrawCollateral(src, to, asset, safe128(amount));
            }
        }
        /**
         * @dev Withdraw an amount of base asset from src to `to`, borrowing if possible/necessary
         */
        function withdrawBase(address src, address to, uint256 amount) internal {
            accrueInternal();
            UserBasic memory srcUser = userBasic[src];
            int104 srcPrincipal = srcUser.principal;
            int256 srcBalance = presentValue(srcPrincipal) - signed256(amount);
            int104 srcPrincipalNew = principalValue(srcBalance);
            (uint104 withdrawAmount, uint104 borrowAmount) = withdrawAndBorrowAmount(srcPrincipal, srcPrincipalNew);
            totalSupplyBase -= withdrawAmount;
            totalBorrowBase += borrowAmount;
            updateBasePrincipal(src, srcUser, srcPrincipalNew);
            if (srcBalance < 0) {
                if (uint256(-srcBalance) < baseBorrowMin) revert BorrowTooSmall();
                if (!isBorrowCollateralized(src)) revert NotCollateralized();
            }
            doTransferOut(baseToken, to, amount);
            emit Withdraw(src, to, amount);
            if (withdrawAmount > 0) {
                emit Transfer(src, address(0), presentValueSupply(baseSupplyIndex, withdrawAmount));
            }
        }
        /**
         * @dev Withdraw an amount of collateral asset from src to `to`
         */
        function withdrawCollateral(address src, address to, address asset, uint128 amount) internal {
            uint128 srcCollateral = userCollateral[src][asset].balance;
            uint128 srcCollateralNew = srcCollateral - amount;
            totalsCollateral[asset].totalSupplyAsset -= amount;
            userCollateral[src][asset].balance = srcCollateralNew;
            AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
            updateAssetsIn(src, assetInfo, srcCollateral, srcCollateralNew);
            // Note: no accrue interest, BorrowCF < LiquidationCF covers small changes
            if (!isBorrowCollateralized(src)) revert NotCollateralized();
            doTransferOut(asset, to, amount);
            emit WithdrawCollateral(src, to, asset, amount);
        }
        /**
         * @notice Absorb a list of underwater accounts onto the protocol balance sheet
         * @param absorber The recipient of the incentive paid to the caller of absorb
         * @param accounts The list of underwater accounts to absorb
         */
        function absorb(address absorber, address[] calldata accounts) override external {
            if (isAbsorbPaused()) revert Paused();
            uint startGas = gasleft();
            accrueInternal();
            for (uint i = 0; i < accounts.length; ) {
                absorbInternal(absorber, accounts[i]);
                unchecked { i++; }
            }
            uint gasUsed = startGas - gasleft();
            // Note: liquidator points are an imperfect tool for governance,
            //  to be used while evaluating strategies for incentivizing absorption.
            // Using gas price instead of base fee would more accurately reflect spend,
            //  but is also subject to abuse if refunds were to be given automatically.
            LiquidatorPoints memory points = liquidatorPoints[absorber];
            points.numAbsorbs++;
            points.numAbsorbed += safe64(accounts.length);
            points.approxSpend += safe128(gasUsed * block.basefee);
            liquidatorPoints[absorber] = points;
        }
        /**
         * @dev Transfer user's collateral and debt to the protocol itself.
         */
        function absorbInternal(address absorber, address account) internal {
            if (!isLiquidatable(account)) revert NotLiquidatable();
            UserBasic memory accountUser = userBasic[account];
            int104 oldPrincipal = accountUser.principal;
            int256 oldBalance = presentValue(oldPrincipal);
            uint16 assetsIn = accountUser.assetsIn;
            uint256 basePrice = getPrice(baseTokenPriceFeed);
            uint256 deltaValue = 0;
            for (uint8 i = 0; i < numAssets; ) {
                if (isInAsset(assetsIn, i)) {
                    AssetInfo memory assetInfo = getAssetInfo(i);
                    address asset = assetInfo.asset;
                    uint128 seizeAmount = userCollateral[account][asset].balance;
                    userCollateral[account][asset].balance = 0;
                    totalsCollateral[asset].totalSupplyAsset -= seizeAmount;
                    uint256 value = mulPrice(seizeAmount, getPrice(assetInfo.priceFeed), assetInfo.scale);
                    deltaValue += mulFactor(value, assetInfo.liquidationFactor);
                    emit AbsorbCollateral(absorber, account, asset, seizeAmount, value);
                }
                unchecked { i++; }
            }
            uint256 deltaBalance = divPrice(deltaValue, basePrice, uint64(baseScale));
            int256 newBalance = oldBalance + signed256(deltaBalance);
            // New balance will not be negative, all excess debt absorbed by reserves
            if (newBalance < 0) {
                newBalance = 0;
            }
            int104 newPrincipal = principalValue(newBalance);
            updateBasePrincipal(account, accountUser, newPrincipal);
            // reset assetsIn
            userBasic[account].assetsIn = 0;
            (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(oldPrincipal, newPrincipal);
            // Reserves are decreased by increasing total supply and decreasing borrows
            //  the amount of debt repaid by reserves is `newBalance - oldBalance`
            totalSupplyBase += supplyAmount;
            totalBorrowBase -= repayAmount;
            uint256 basePaidOut = unsigned256(newBalance - oldBalance);
            uint256 valueOfBasePaidOut = mulPrice(basePaidOut, basePrice, uint64(baseScale));
            emit AbsorbDebt(absorber, account, basePaidOut, valueOfBasePaidOut);
            if (newPrincipal > 0) {
                emit Transfer(address(0), account, presentValueSupply(baseSupplyIndex, unsigned104(newPrincipal)));
            }
        }
        /**
         * @notice Buy collateral from the protocol using base tokens, increasing protocol reserves
           A minimum collateral amount should be specified to indicate the maximum slippage acceptable for the buyer.
         * @param asset The asset to buy
         * @param minAmount The minimum amount of collateral tokens that should be received by the buyer
         * @param baseAmount The amount of base tokens used to buy the collateral
         * @param recipient The recipient address
         */
        function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) override external {
            if (isBuyPaused()) revert Paused();
            int reserves = getReserves();
            if (reserves >= 0 && uint(reserves) >= targetReserves) revert NotForSale();
            // Note: Re-entrancy can skip the reserves check above on a second buyCollateral call.
            doTransferIn(baseToken, msg.sender, baseAmount);
            uint collateralAmount = quoteCollateral(asset, baseAmount);
            if (collateralAmount < minAmount) revert TooMuchSlippage();
            if (collateralAmount > getCollateralReserves(asset)) revert InsufficientReserves();
            // Note: Pre-transfer hook can re-enter buyCollateral with a stale collateral ERC20 balance.
            //  Assets should not be listed which allow re-entry from pre-transfer now, as too much collateral could be bought.
            //  This is also a problem if quoteCollateral derives its discount from the collateral ERC20 balance.
            doTransferOut(asset, recipient, safe128(collateralAmount));
            emit BuyCollateral(msg.sender, asset, baseAmount, collateralAmount);
        }
        /**
         * @notice Gets the quote for a collateral asset in exchange for an amount of base asset
         * @param asset The collateral asset to get the quote for
         * @param baseAmount The amount of the base asset to get the quote for
         * @return The quote in terms of the collateral asset
         */
        function quoteCollateral(address asset, uint baseAmount) override public view returns (uint) {
            AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
            uint256 assetPrice = getPrice(assetInfo.priceFeed);
            // Store front discount is derived from the collateral asset's liquidationFactor and storeFrontPriceFactor
            // discount = storeFrontPriceFactor * (1e18 - liquidationFactor)
            uint256 discountFactor = mulFactor(storeFrontPriceFactor, FACTOR_SCALE - assetInfo.liquidationFactor);
            uint256 assetPriceDiscounted = mulFactor(assetPrice, FACTOR_SCALE - discountFactor);
            uint256 basePrice = getPrice(baseTokenPriceFeed);
            // # of collateral assets
            // = (TotalValueOfBaseAmount / DiscountedPriceOfCollateralAsset) * assetScale
            // = ((basePrice * baseAmount / baseScale) / assetPriceDiscounted) * assetScale
            return basePrice * baseAmount * assetInfo.scale / assetPriceDiscounted / baseScale;
        }
        /**
         * @notice Withdraws base token reserves if called by the governor
         * @param to An address of the receiver of withdrawn reserves
         * @param amount The amount of reserves to be withdrawn from the protocol
         */
        function withdrawReserves(address to, uint amount) override external {
            if (msg.sender != governor) revert Unauthorized();
            int reserves = getReserves();
            if (reserves < 0 || amount > unsigned256(reserves)) revert InsufficientReserves();
            doTransferOut(baseToken, to, amount);
            emit WithdrawReserves(to, amount);
        }
        /**
         * @notice Sets Comet's ERC20 allowance of an asset for a manager
         * @dev Only callable by governor
         * @dev Note: Setting the `asset` as Comet's address will allow the manager
         * to withdraw from Comet's Comet balance
         * @param asset The asset that the manager will gain approval of
         * @param manager The account which will be allowed or disallowed
         * @param amount The amount of an asset to approve
         */
        function approveThis(address manager, address asset, uint amount) override external {
            if (msg.sender != governor) revert Unauthorized();
            ERC20(asset).approve(manager, amount);
        }
        /**
         * @notice Get the total number of tokens in circulation
         * @dev Note: uses updated interest indices to calculate
         * @return The supply of tokens
         **/
        function totalSupply() override external view returns (uint256) {
            (uint64 baseSupplyIndex_, ) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
            return presentValueSupply(baseSupplyIndex_, totalSupplyBase);
        }
        /**
         * @notice Get the total amount of debt
         * @dev Note: uses updated interest indices to calculate
         * @return The amount of debt
         **/
        function totalBorrow() override external view returns (uint256) {
            (, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
            return presentValueBorrow(baseBorrowIndex_, totalBorrowBase);
        }
        /**
         * @notice Query the current positive base balance of an account or zero
         * @dev Note: uses updated interest indices to calculate
         * @param account The account whose balance to query
         * @return The present day base balance magnitude of the account, if positive
         */
        function balanceOf(address account) override public view returns (uint256) {
            (uint64 baseSupplyIndex_, ) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
            int104 principal = userBasic[account].principal;
            return principal > 0 ? presentValueSupply(baseSupplyIndex_, unsigned104(principal)) : 0;
        }
        /**
         * @notice Query the current negative base balance of an account or zero
         * @dev Note: uses updated interest indices to calculate
         * @param account The account whose balance to query
         * @return The present day base balance magnitude of the account, if negative
         */
        function borrowBalanceOf(address account) override public view returns (uint256) {
            (, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
            int104 principal = userBasic[account].principal;
            return principal < 0 ? presentValueBorrow(baseBorrowIndex_, unsigned104(-principal)) : 0;
        }
        /**
         * @notice Fallback to calling the extension delegate for everything else
         */
        fallback() external payable {
            address delegate = extensionDelegate;
            assembly {
                calldatacopy(0, 0, calldatasize())
                let result := delegatecall(gas(), delegate, 0, calldatasize(), 0, 0)
                returndatacopy(0, 0, returndatasize())
                switch result
                case 0 { revert(0, returndatasize()) }
                default { return(0, returndatasize()) }
            }
        }
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.15;
    import "./CometCore.sol";
    /**
     * @title Compound's Comet Main Interface (without Ext)
     * @notice An efficient monolithic money market protocol
     * @author Compound
     */
    abstract contract CometMainInterface is CometCore {
        error Absurd();
        error AlreadyInitialized();
        error BadAsset();
        error BadDecimals();
        error BadDiscount();
        error BadMinimum();
        error BadPrice();
        error BorrowTooSmall();
        error BorrowCFTooLarge();
        error InsufficientReserves();
        error LiquidateCFTooLarge();
        error NoSelfTransfer();
        error NotCollateralized();
        error NotForSale();
        error NotLiquidatable();
        error Paused();
        error SupplyCapExceeded();
        error TimestampTooLarge();
        error TooManyAssets();
        error TooMuchSlippage();
        error TransferInFailed();
        error TransferOutFailed();
        error Unauthorized();
        event Supply(address indexed from, address indexed dst, uint amount);
        event Transfer(address indexed from, address indexed to, uint amount);
        event Withdraw(address indexed src, address indexed to, uint amount);
        event SupplyCollateral(address indexed from, address indexed dst, address indexed asset, uint amount);
        event TransferCollateral(address indexed from, address indexed to, address indexed asset, uint amount);
        event WithdrawCollateral(address indexed src, address indexed to, address indexed asset, uint amount);
        /// @notice Event emitted when a borrow position is absorbed by the protocol
        event AbsorbDebt(address indexed absorber, address indexed borrower, uint basePaidOut, uint usdValue);
        /// @notice Event emitted when a user's collateral is absorbed by the protocol
        event AbsorbCollateral(address indexed absorber, address indexed borrower, address indexed asset, uint collateralAbsorbed, uint usdValue);
        /// @notice Event emitted when a collateral asset is purchased from the protocol
        event BuyCollateral(address indexed buyer, address indexed asset, uint baseAmount, uint collateralAmount);
        /// @notice Event emitted when an action is paused/unpaused
        event PauseAction(bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused);
        /// @notice Event emitted when reserves are withdrawn by the governor
        event WithdrawReserves(address indexed to, uint amount);
        function supply(address asset, uint amount) virtual external;
        function supplyTo(address dst, address asset, uint amount) virtual external;
        function supplyFrom(address from, address dst, address asset, uint amount) virtual external;
        function transfer(address dst, uint amount) virtual external returns (bool);
        function transferFrom(address src, address dst, uint amount) virtual external returns (bool);
        function transferAsset(address dst, address asset, uint amount) virtual external;
        function transferAssetFrom(address src, address dst, address asset, uint amount) virtual external;
        function withdraw(address asset, uint amount) virtual external;
        function withdrawTo(address to, address asset, uint amount) virtual external;
        function withdrawFrom(address src, address to, address asset, uint amount) virtual external;
        function approveThis(address manager, address asset, uint amount) virtual external;
        function withdrawReserves(address to, uint amount) virtual external;
        function absorb(address absorber, address[] calldata accounts) virtual external;
        function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) virtual external;
        function quoteCollateral(address asset, uint baseAmount) virtual public view returns (uint);
        function getAssetInfo(uint8 i) virtual public view returns (AssetInfo memory);
        function getAssetInfoByAddress(address asset) virtual public view returns (AssetInfo memory);
        function getCollateralReserves(address asset) virtual public view returns (uint);
        function getReserves() virtual public view returns (int);
        function getPrice(address priceFeed) virtual public view returns (uint);
        function isBorrowCollateralized(address account) virtual public view returns (bool);
        function isLiquidatable(address account) virtual public view returns (bool);
        function totalSupply() virtual external view returns (uint256);
        function totalBorrow() virtual external view returns (uint256);
        function balanceOf(address owner) virtual public view returns (uint256);
        function borrowBalanceOf(address account) virtual public view returns (uint256);
        function pause(bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused) virtual external;
        function isSupplyPaused() virtual public view returns (bool);
        function isTransferPaused() virtual public view returns (bool);
        function isWithdrawPaused() virtual public view returns (bool);
        function isAbsorbPaused() virtual public view returns (bool);
        function isBuyPaused() virtual public view returns (bool);
        function accrueAccount(address account) virtual external;
        function getSupplyRate(uint utilization) virtual public view returns (uint64);
        function getBorrowRate(uint utilization) virtual public view returns (uint64);
        function getUtilization() virtual public view returns (uint);
        function governor() virtual external view returns (address);
        function pauseGuardian() virtual external view returns (address);
        function baseToken() virtual external view returns (address);
        function baseTokenPriceFeed() virtual external view returns (address);
        function extensionDelegate() virtual external view returns (address);
        /// @dev uint64
        function supplyKink() virtual external view returns (uint);
        /// @dev uint64
        function supplyPerSecondInterestRateSlopeLow() virtual external view returns (uint);
        /// @dev uint64
        function supplyPerSecondInterestRateSlopeHigh() virtual external view returns (uint);
        /// @dev uint64
        function supplyPerSecondInterestRateBase() virtual external view returns (uint);
        /// @dev uint64
        function borrowKink() virtual external view returns (uint);
        /// @dev uint64
        function borrowPerSecondInterestRateSlopeLow() virtual external view returns (uint);
        /// @dev uint64
        function borrowPerSecondInterestRateSlopeHigh() virtual external view returns (uint);
        /// @dev uint64
        function borrowPerSecondInterestRateBase() virtual external view returns (uint);
        /// @dev uint64
        function storeFrontPriceFactor() virtual external view returns (uint);
        /// @dev uint64
        function baseScale() virtual external view returns (uint);
        /// @dev uint64
        function trackingIndexScale() virtual external view returns (uint);
        /// @dev uint64
        function baseTrackingSupplySpeed() virtual external view returns (uint);
        /// @dev uint64
        function baseTrackingBorrowSpeed() virtual external view returns (uint);
        /// @dev uint104
        function baseMinForRewards() virtual external view returns (uint);
        /// @dev uint104
        function baseBorrowMin() virtual external view returns (uint);
        /// @dev uint104
        function targetReserves() virtual external view returns (uint);
        function numAssets() virtual external view returns (uint8);
        function decimals() virtual external view returns (uint8);
        function initializeStorage() virtual external;
    }// SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.15;
    /**
     * @title ERC 20 Token Standard Interface
     *  https://eips.ethereum.org/EIPS/eip-20
     */
    interface ERC20 {
        function name() external view returns (string memory);
        function symbol() external view returns (string memory);
        function decimals() external view returns (uint8);
        /**
          * @notice Get the total number of tokens in circulation
          * @return The supply of tokens
          */
        function totalSupply() external view returns (uint256);
        /**
         * @notice Gets the balance of the specified address
         * @param owner The address from which the balance will be retrieved
         * @return The balance
         */
        function balanceOf(address owner) external view returns (uint256);
        /**
          * @notice Transfer `amount` tokens from `msg.sender` to `dst`
          * @param dst The address of the destination account
          * @param amount The number of tokens to transfer
          * @return Whether or not the transfer succeeded
          */
        function transfer(address dst, uint256 amount) external returns (bool);
        /**
          * @notice Transfer `amount` tokens from `src` to `dst`
          * @param src The address of the source account
          * @param dst The address of the destination account
          * @param amount The number of tokens to transfer
          * @return Whether or not the transfer succeeded
          */
        function transferFrom(address src, address dst, uint256 amount) external returns (bool);
        /**
          * @notice Approve `spender` to transfer up to `amount` from `src`
          * @dev This will overwrite the approval amount for `spender`
          *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
          * @param spender The address of the account which may transfer tokens
          * @param amount The number of tokens that are approved (-1 means infinite)
          * @return Whether or not the approval succeeded
          */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
          * @notice Get the current allowance from `owner` for `spender`
          * @param owner The address of the account which owns the tokens to be spent
          * @param spender The address of the account which may transfer tokens
          * @return The number of tokens allowed to be spent (-1 means infinite)
          */
        function allowance(address owner, address spender) external view returns (uint256);
        event Transfer(address indexed from, address indexed to, uint256 amount);
        event Approval(address indexed owner, address indexed spender, uint256 amount);
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.15;
    /**
     * @dev Interface for price feeds used by Comet
     * Note This is Chainlink's AggregatorV3Interface, but without the `getRoundData` function.
     */
    interface IPriceFeed {
      function decimals() external view returns (uint8);
      function description() external view returns (string memory);
      function version() external view returns (uint256);
      function latestRoundData()
        external
        view
        returns (
          uint80 roundId,
          int256 answer,
          uint256 startedAt,
          uint256 updatedAt,
          uint80 answeredInRound
        );
    }// SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.15;
    import "./CometConfiguration.sol";
    import "./CometStorage.sol";
    import "./CometMath.sol";
    abstract contract CometCore is CometConfiguration, CometStorage, CometMath {
        struct AssetInfo {
            uint8 offset;
            address asset;
            address priceFeed;
            uint64 scale;
            uint64 borrowCollateralFactor;
            uint64 liquidateCollateralFactor;
            uint64 liquidationFactor;
            uint128 supplyCap;
        }
        /** Internal constants **/
        /// @dev The max number of assets this contract is hardcoded to support
        ///  Do not change this variable without updating all the fields throughout the contract,
        //    including the size of UserBasic.assetsIn and corresponding integer conversions.
        uint8 internal constant MAX_ASSETS = 15;
        /// @dev The max number of decimals base token can have
        ///  Note this cannot just be increased arbitrarily.
        uint8 internal constant MAX_BASE_DECIMALS = 18;
        /// @dev The max value for a collateral factor (1)
        uint64 internal constant MAX_COLLATERAL_FACTOR = FACTOR_SCALE;
        /// @dev Offsets for specific actions in the pause flag bit array
        uint8 internal constant PAUSE_SUPPLY_OFFSET = 0;
        uint8 internal constant PAUSE_TRANSFER_OFFSET = 1;
        uint8 internal constant PAUSE_WITHDRAW_OFFSET = 2;
        uint8 internal constant PAUSE_ABSORB_OFFSET = 3;
        uint8 internal constant PAUSE_BUY_OFFSET = 4;
        /// @dev The decimals required for a price feed
        uint8 internal constant PRICE_FEED_DECIMALS = 8;
        /// @dev 365 days * 24 hours * 60 minutes * 60 seconds
        uint64 internal constant SECONDS_PER_YEAR = 31_536_000;
        /// @dev The scale for base tracking accrual
        uint64 internal constant BASE_ACCRUAL_SCALE = 1e6;
        /// @dev The scale for base index (depends on time/rate scales, not base token)
        uint64 internal constant BASE_INDEX_SCALE = 1e15;
        /// @dev The scale for prices (in USD)
        uint64 internal constant PRICE_SCALE = uint64(10 ** PRICE_FEED_DECIMALS);
        /// @dev The scale for factors
        uint64 internal constant FACTOR_SCALE = 1e18;
        /**
         * @notice Determine if the manager has permission to act on behalf of the owner
         * @param owner The owner account
         * @param manager The manager account
         * @return Whether or not the manager has permission
         */
        function hasPermission(address owner, address manager) public view returns (bool) {
            return owner == manager || isAllowed[owner][manager];
        }
        /**
         * @dev The positive present supply balance if positive or the negative borrow balance if negative
         */
        function presentValue(int104 principalValue_) internal view returns (int256) {
            if (principalValue_ >= 0) {
                return signed256(presentValueSupply(baseSupplyIndex, uint104(principalValue_)));
            } else {
                return -signed256(presentValueBorrow(baseBorrowIndex, uint104(-principalValue_)));
            }
        }
        /**
         * @dev The principal amount projected forward by the supply index
         */
        function presentValueSupply(uint64 baseSupplyIndex_, uint104 principalValue_) internal pure returns (uint256) {
            return uint256(principalValue_) * baseSupplyIndex_ / BASE_INDEX_SCALE;
        }
        /**
         * @dev The principal amount projected forward by the borrow index
         */
        function presentValueBorrow(uint64 baseBorrowIndex_, uint104 principalValue_) internal pure returns (uint256) {
            return uint256(principalValue_) * baseBorrowIndex_ / BASE_INDEX_SCALE;
        }
        /**
         * @dev The positive principal if positive or the negative principal if negative
         */
        function principalValue(int256 presentValue_) internal view returns (int104) {
            if (presentValue_ >= 0) {
                return signed104(principalValueSupply(baseSupplyIndex, uint256(presentValue_)));
            } else {
                return -signed104(principalValueBorrow(baseBorrowIndex, uint256(-presentValue_)));
            }
        }
        /**
         * @dev The present value projected backward by the supply index (rounded down)
         *  Note: This will overflow (revert) at 2^104/1e18=~20 trillion principal for assets with 18 decimals.
         */
        function principalValueSupply(uint64 baseSupplyIndex_, uint256 presentValue_) internal pure returns (uint104) {
            return safe104((presentValue_ * BASE_INDEX_SCALE) / baseSupplyIndex_);
        }
        /**
         * @dev The present value projected backward by the borrow index (rounded up)
         *  Note: This will overflow (revert) at 2^104/1e18=~20 trillion principal for assets with 18 decimals.
         */
        function principalValueBorrow(uint64 baseBorrowIndex_, uint256 presentValue_) internal pure returns (uint104) {
            return safe104((presentValue_ * BASE_INDEX_SCALE + baseBorrowIndex_ - 1) / baseBorrowIndex_);
        }
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.15;
    /**
     * @title Compound's Comet Configuration Interface
     * @author Compound
     */
    contract CometConfiguration {
        struct ExtConfiguration {
            bytes32 name32;
            bytes32 symbol32;
        }
        struct Configuration {
            address governor;
            address pauseGuardian;
            address baseToken;
            address baseTokenPriceFeed;
            address extensionDelegate;
            uint64 supplyKink;
            uint64 supplyPerYearInterestRateSlopeLow;
            uint64 supplyPerYearInterestRateSlopeHigh;
            uint64 supplyPerYearInterestRateBase;
            uint64 borrowKink;
            uint64 borrowPerYearInterestRateSlopeLow;
            uint64 borrowPerYearInterestRateSlopeHigh;
            uint64 borrowPerYearInterestRateBase;
            uint64 storeFrontPriceFactor;
            uint64 trackingIndexScale;
            uint64 baseTrackingSupplySpeed;
            uint64 baseTrackingBorrowSpeed;
            uint104 baseMinForRewards;
            uint104 baseBorrowMin;
            uint104 targetReserves;
            AssetConfig[] assetConfigs;
        }
        struct AssetConfig {
            address asset;
            address priceFeed;
            uint8 decimals;
            uint64 borrowCollateralFactor;
            uint64 liquidateCollateralFactor;
            uint64 liquidationFactor;
            uint128 supplyCap;
        }
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.15;
    /**
     * @title Compound's Comet Storage Interface
     * @dev Versions can enforce append-only storage slots via inheritance.
     * @author Compound
     */
    contract CometStorage {
        // 512 bits total = 2 slots
        struct TotalsBasic {
            // 1st slot
            uint64 baseSupplyIndex;
            uint64 baseBorrowIndex;
            uint64 trackingSupplyIndex;
            uint64 trackingBorrowIndex;
            // 2nd slot
            uint104 totalSupplyBase;
            uint104 totalBorrowBase;
            uint40 lastAccrualTime;
            uint8 pauseFlags;
        }
        struct TotalsCollateral {
            uint128 totalSupplyAsset;
            uint128 _reserved;
        }
        struct UserBasic {
            int104 principal;
            uint64 baseTrackingIndex;
            uint64 baseTrackingAccrued;
            uint16 assetsIn;
            uint8 _reserved;
        }
        struct UserCollateral {
            uint128 balance;
            uint128 _reserved;
        }
        struct LiquidatorPoints {
            uint32 numAbsorbs;
            uint64 numAbsorbed;
            uint128 approxSpend;
            uint32 _reserved;
        }
        /// @dev Aggregate variables tracked for the entire market
        uint64 internal baseSupplyIndex;
        uint64 internal baseBorrowIndex;
        uint64 internal trackingSupplyIndex;
        uint64 internal trackingBorrowIndex;
        uint104 internal totalSupplyBase;
        uint104 internal totalBorrowBase;
        uint40 internal lastAccrualTime;
        uint8 internal pauseFlags;
        /// @notice Aggregate variables tracked for each collateral asset
        mapping(address => TotalsCollateral) public totalsCollateral;
        /// @notice Mapping of users to accounts which may be permitted to manage the user account
        mapping(address => mapping(address => bool)) public isAllowed;
        /// @notice The next expected nonce for an address, for validating authorizations via signature
        mapping(address => uint) public userNonce;
        /// @notice Mapping of users to base principal and other basic data
        mapping(address => UserBasic) public userBasic;
        /// @notice Mapping of users to collateral data per collateral asset
        mapping(address => mapping(address => UserCollateral)) public userCollateral;
        /// @notice Mapping of magic liquidator points
        mapping(address => LiquidatorPoints) public liquidatorPoints;
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.15;
    /**
     * @title Compound's Comet Math Contract
     * @dev Pure math functions
     * @author Compound
     */
    contract CometMath {
        /** Custom errors **/
        error InvalidUInt64();
        error InvalidUInt104();
        error InvalidUInt128();
        error InvalidInt104();
        error InvalidInt256();
        error NegativeNumber();
        function safe64(uint n) internal pure returns (uint64) {
            if (n > type(uint64).max) revert InvalidUInt64();
            return uint64(n);
        }
        function safe104(uint n) internal pure returns (uint104) {
            if (n > type(uint104).max) revert InvalidUInt104();
            return uint104(n);
        }
        function safe128(uint n) internal pure returns (uint128) {
            if (n > type(uint128).max) revert InvalidUInt128();
            return uint128(n);
        }
        function signed104(uint104 n) internal pure returns (int104) {
            if (n > uint104(type(int104).max)) revert InvalidInt104();
            return int104(n);
        }
        function signed256(uint256 n) internal pure returns (int256) {
            if (n > uint256(type(int256).max)) revert InvalidInt256();
            return int256(n);
        }
        function unsigned104(int104 n) internal pure returns (uint104) {
            if (n < 0) revert NegativeNumber();
            return uint104(n);
        }
        function unsigned256(int256 n) internal pure returns (uint256) {
            if (n < 0) revert NegativeNumber();
            return uint256(n);
        }
        function toUInt8(bool x) internal pure returns (uint8) {
            return x ? 1 : 0;
        }
        function toBool(uint8 x) internal pure returns (bool) {
            return x != 0;
        }
    }