ETH Price: $2,572.09 (+1.96%)

Transaction Decoder

Block:
22854030 at Jul-05-2025 03:52:23 PM +UTC
Transaction Fee:
0.000127566 ETH $0.33
Gas Used:
127,566 Gas / 1 Gwei

Emitted Events:

406 WBTC.Transfer( from=[Sender] 0x77397d0cfc09d7cb901a01d8b3022a04f0b1b4fb, to=[Receiver] TransparentUpgradeableProxy, value=100140000 )
407 TransparentUpgradeableProxy.0xfa56f7b24f17183d81894d3ac2ee654e3c26388d17a28dbd9549b8114304e1f4( 0xfa56f7b24f17183d81894d3ac2ee654e3c26388d17a28dbd9549b8114304e1f4, 0x00000000000000000000000077397d0cfc09d7cb901a01d8b3022a04f0b1b4fb, 0x00000000000000000000000077397d0cfc09d7cb901a01d8b3022a04f0b1b4fb, 0x0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599, 0000000000000000000000000000000000000000000000000000000005f803e0 )

Account State Difference:

  Address   Before After State Difference Code
0x2260FAC5...93bc2C599
0x3Afdc9BC...0d5AB0840
0x77397D0C...4f0B1B4FB
0.02744458530757987 Eth
Nonce: 1
0.02731701930757987 Eth
Nonce: 2
0.000127566
(beaverbuild)
19.336055605226583349 Eth19.336121928301257751 Eth0.000066323074674402

Execution Trace

TransparentUpgradeableProxy.f2b9fdb8( )
  • CometWithExtendedAssetList.supply( asset=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, amount=100140000 )
    • WBTC.balanceOf( _owner=0x3Afdc9BCA9213A35503b077a6072F3D0d5AB0840 ) => ( 158516401262 )
    • WBTC.transferFrom( _from=0x77397D0CFc09d7cb901A01d8B3022a04f0B1B4FB, _to=0x3Afdc9BCA9213A35503b077a6072F3D0d5AB0840, _value=100140000 ) => ( True )
    • WBTC.balanceOf( _owner=0x3Afdc9BCA9213A35503b077a6072F3D0d5AB0840 ) => ( 158616541262 )
    • AssetList.getAssetInfo( i=0 ) => ( [{name:offset, type:uint8, order:1, indexed:false, value:0, valueString:0}, {name:asset, type:address, order:2, indexed:false, value:0xc00e94Cb662C3520282E6f5717214004A7f26888, valueString:0xc00e94Cb662C3520282E6f5717214004A7f26888}, {name:priceFeed, type:address, order:3, indexed:false, value:0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5, valueString:0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5}, {name:scale, type:uint64, order:4, indexed:false, value:1000000000000000000, valueString:1000000000000000000}, {name:borrowCollateralFactor, type:uint64, order:5, indexed:false, value:500000000000000000, valueString:500000000000000000}, {name:liquidateCollateralFactor, type:uint64, order:6, indexed:false, value:700000000000000000, valueString:700000000000000000}, {name:liquidationFactor, type:uint64, order:7, indexed:false, value:750000000000000000, valueString:750000000000000000}, {name:supplyCap, type:uint128, order:8, indexed:false, value:100000000000000000000000, valueString:100000000000000000000000}] )
    • AssetList.getAssetInfo( i=1 ) => ( [{name:offset, type:uint8, order:1, indexed:false, value:1, valueString:1}, {name:asset, type:address, order:2, indexed:false, value:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, valueString:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}, {name:priceFeed, type:address, order:3, indexed:false, value:0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419, valueString:0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419}, {name:scale, type:uint64, order:4, indexed:false, value:1000000000000000000, valueString:1000000000000000000}, {name:borrowCollateralFactor, type:uint64, order:5, indexed:false, value:830000000000000000, valueString:830000000000000000}, {name:liquidateCollateralFactor, type:uint64, order:6, indexed:false, value:900000000000000000, valueString:900000000000000000}, {name:liquidationFactor, type:uint64, order:7, indexed:false, value:950000000000000000, valueString:950000000000000000}, {name:supplyCap, type:uint128, order:8, indexed:false, value:500000000000000000000000, valueString:500000000000000000000000}] )
    • AssetList.getAssetInfo( i=2 ) => ( [{name:offset, type:uint8, order:1, indexed:false, value:2, valueString:2}, {name:asset, type:address, order:2, indexed:false, value:0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, valueString:0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599}, {name:priceFeed, type:address, order:3, indexed:false, value:0x4E64E54c9f0313852a230782B3ba4B3B0952B499, valueString:0x4E64E54c9f0313852a230782B3ba4B3B0952B499}, {name:scale, type:uint64, order:4, indexed:false, value:100000000, valueString:100000000}, {name:borrowCollateralFactor, type:uint64, order:5, indexed:false, value:800000000000000000, valueString:800000000000000000}, {name:liquidateCollateralFactor, type:uint64, order:6, indexed:false, value:850000000000000000, valueString:850000000000000000}, {name:liquidationFactor, type:uint64, order:7, indexed:false, value:900000000000000000, valueString:900000000000000000}, {name:supplyCap, type:uint128, order:8, indexed:false, value:250000000000, valueString:250000000000}] )
      File 1 of 4: 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 4: WBTC
      pragma solidity 0.4.24;
      
      // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
      
      /**
       * @title ERC20Basic
       * @dev Simpler version of ERC20 interface
       * See https://github.com/ethereum/EIPs/issues/179
       */
      contract ERC20Basic {
        function totalSupply() public view returns (uint256);
        function balanceOf(address _who) public view returns (uint256);
        function transfer(address _to, uint256 _value) public returns (bool);
        event Transfer(address indexed from, address indexed to, uint256 value);
      }
      
      // File: openzeppelin-solidity/contracts/math/SafeMath.sol
      
      /**
       * @title SafeMath
       * @dev Math operations with safety checks that throw on error
       */
      library SafeMath {
      
        /**
        * @dev Multiplies two numbers, throws on overflow.
        */
        function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
          // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
          // benefit is lost if 'b' is also tested.
          // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
          if (_a == 0) {
            return 0;
          }
      
          c = _a * _b;
          assert(c / _a == _b);
          return c;
        }
      
        /**
        * @dev Integer division of two numbers, truncating the quotient.
        */
        function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
          // assert(_b > 0); // Solidity automatically throws when dividing by 0
          // uint256 c = _a / _b;
          // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
          return _a / _b;
        }
      
        /**
        * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
        */
        function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
          assert(_b <= _a);
          return _a - _b;
        }
      
        /**
        * @dev Adds two numbers, throws on overflow.
        */
        function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
          c = _a + _b;
          assert(c >= _a);
          return c;
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/BasicToken.sol
      
      /**
       * @title Basic token
       * @dev Basic version of StandardToken, with no allowances.
       */
      contract BasicToken is ERC20Basic {
        using SafeMath for uint256;
      
        mapping(address => uint256) internal balances;
      
        uint256 internal totalSupply_;
      
        /**
        * @dev Total number of tokens in existence
        */
        function totalSupply() public view returns (uint256) {
          return totalSupply_;
        }
      
        /**
        * @dev Transfer token for a specified address
        * @param _to The address to transfer to.
        * @param _value The amount to be transferred.
        */
        function transfer(address _to, uint256 _value) public returns (bool) {
          require(_value <= balances[msg.sender]);
          require(_to != address(0));
      
          balances[msg.sender] = balances[msg.sender].sub(_value);
          balances[_to] = balances[_to].add(_value);
          emit Transfer(msg.sender, _to, _value);
          return true;
        }
      
        /**
        * @dev Gets the balance of the specified address.
        * @param _owner The address to query the the balance of.
        * @return An uint256 representing the amount owned by the passed address.
        */
        function balanceOf(address _owner) public view returns (uint256) {
          return balances[_owner];
        }
      
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
      
      /**
       * @title ERC20 interface
       * @dev see https://github.com/ethereum/EIPs/issues/20
       */
      contract ERC20 is ERC20Basic {
        function allowance(address _owner, address _spender)
          public view returns (uint256);
      
        function transferFrom(address _from, address _to, uint256 _value)
          public returns (bool);
      
        function approve(address _spender, uint256 _value) public returns (bool);
        event Approval(
          address indexed owner,
          address indexed spender,
          uint256 value
        );
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol
      
      /**
       * @title Standard ERC20 token
       *
       * @dev Implementation of the basic standard token.
       * https://github.com/ethereum/EIPs/issues/20
       * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
       */
      contract StandardToken is ERC20, BasicToken {
      
        mapping (address => mapping (address => uint256)) internal allowed;
      
      
        /**
         * @dev Transfer tokens from one address to another
         * @param _from address The address which you want to send tokens from
         * @param _to address The address which you want to transfer to
         * @param _value uint256 the amount of tokens to be transferred
         */
        function transferFrom(
          address _from,
          address _to,
          uint256 _value
        )
          public
          returns (bool)
        {
          require(_value <= balances[_from]);
          require(_value <= allowed[_from][msg.sender]);
          require(_to != address(0));
      
          balances[_from] = balances[_from].sub(_value);
          balances[_to] = balances[_to].add(_value);
          allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
          emit Transfer(_from, _to, _value);
          return true;
        }
      
        /**
         * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
         * Beware that changing an allowance with this method brings the risk that someone may use both the old
         * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
         * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         * @param _spender The address which will spend the funds.
         * @param _value The amount of tokens to be spent.
         */
        function approve(address _spender, uint256 _value) public returns (bool) {
          allowed[msg.sender][_spender] = _value;
          emit Approval(msg.sender, _spender, _value);
          return true;
        }
      
        /**
         * @dev Function to check the amount of tokens that an owner allowed to a spender.
         * @param _owner address The address which owns the funds.
         * @param _spender address The address which will spend the funds.
         * @return A uint256 specifying the amount of tokens still available for the spender.
         */
        function allowance(
          address _owner,
          address _spender
         )
          public
          view
          returns (uint256)
        {
          return allowed[_owner][_spender];
        }
      
        /**
         * @dev Increase the amount of tokens that an owner allowed to a spender.
         * approve should be called when allowed[_spender] == 0. To increment
         * allowed value is better to use this function to avoid 2 calls (and wait until
         * the first transaction is mined)
         * From MonolithDAO Token.sol
         * @param _spender The address which will spend the funds.
         * @param _addedValue The amount of tokens to increase the allowance by.
         */
        function increaseApproval(
          address _spender,
          uint256 _addedValue
        )
          public
          returns (bool)
        {
          allowed[msg.sender][_spender] = (
            allowed[msg.sender][_spender].add(_addedValue));
          emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
          return true;
        }
      
        /**
         * @dev Decrease the amount of tokens that an owner allowed to a spender.
         * approve should be called when allowed[_spender] == 0. To decrement
         * allowed value is better to use this function to avoid 2 calls (and wait until
         * the first transaction is mined)
         * From MonolithDAO Token.sol
         * @param _spender The address which will spend the funds.
         * @param _subtractedValue The amount of tokens to decrease the allowance by.
         */
        function decreaseApproval(
          address _spender,
          uint256 _subtractedValue
        )
          public
          returns (bool)
        {
          uint256 oldValue = allowed[msg.sender][_spender];
          if (_subtractedValue >= oldValue) {
            allowed[msg.sender][_spender] = 0;
          } else {
            allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
          }
          emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
          return true;
        }
      
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol
      
      /**
       * @title DetailedERC20 token
       * @dev The decimals are only for visualization purposes.
       * All the operations are done using the smallest and indivisible token unit,
       * just as on Ethereum all the operations are done in wei.
       */
      contract DetailedERC20 is ERC20 {
        string public name;
        string public symbol;
        uint8 public decimals;
      
        constructor(string _name, string _symbol, uint8 _decimals) public {
          name = _name;
          symbol = _symbol;
          decimals = _decimals;
        }
      }
      
      // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
      
      /**
       * @title Ownable
       * @dev The Ownable contract has an owner address, and provides basic authorization control
       * functions, this simplifies the implementation of "user permissions".
       */
      contract Ownable {
        address public owner;
      
      
        event OwnershipRenounced(address indexed previousOwner);
        event OwnershipTransferred(
          address indexed previousOwner,
          address indexed newOwner
        );
      
      
        /**
         * @dev The Ownable constructor sets the original `owner` of the contract to the sender
         * account.
         */
        constructor() public {
          owner = msg.sender;
        }
      
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
          require(msg.sender == owner);
          _;
        }
      
        /**
         * @dev Allows the current owner to relinquish control of the contract.
         * @notice Renouncing to ownership will leave the contract without an owner.
         * It will not be possible to call the functions with the `onlyOwner`
         * modifier anymore.
         */
        function renounceOwnership() public onlyOwner {
          emit OwnershipRenounced(owner);
          owner = address(0);
        }
      
        /**
         * @dev Allows the current owner to transfer control of the contract to a newOwner.
         * @param _newOwner The address to transfer ownership to.
         */
        function transferOwnership(address _newOwner) public onlyOwner {
          _transferOwnership(_newOwner);
        }
      
        /**
         * @dev Transfers control of the contract to a newOwner.
         * @param _newOwner The address to transfer ownership to.
         */
        function _transferOwnership(address _newOwner) internal {
          require(_newOwner != address(0));
          emit OwnershipTransferred(owner, _newOwner);
          owner = _newOwner;
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol
      
      /**
       * @title Mintable token
       * @dev Simple ERC20 Token example, with mintable token creation
       * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol
       */
      contract MintableToken is StandardToken, Ownable {
        event Mint(address indexed to, uint256 amount);
        event MintFinished();
      
        bool public mintingFinished = false;
      
      
        modifier canMint() {
          require(!mintingFinished);
          _;
        }
      
        modifier hasMintPermission() {
          require(msg.sender == owner);
          _;
        }
      
        /**
         * @dev Function to mint tokens
         * @param _to The address that will receive the minted tokens.
         * @param _amount The amount of tokens to mint.
         * @return A boolean that indicates if the operation was successful.
         */
        function mint(
          address _to,
          uint256 _amount
        )
          public
          hasMintPermission
          canMint
          returns (bool)
        {
          totalSupply_ = totalSupply_.add(_amount);
          balances[_to] = balances[_to].add(_amount);
          emit Mint(_to, _amount);
          emit Transfer(address(0), _to, _amount);
          return true;
        }
      
        /**
         * @dev Function to stop minting new tokens.
         * @return True if the operation was successful.
         */
        function finishMinting() public onlyOwner canMint returns (bool) {
          mintingFinished = true;
          emit MintFinished();
          return true;
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/BurnableToken.sol
      
      /**
       * @title Burnable Token
       * @dev Token that can be irreversibly burned (destroyed).
       */
      contract BurnableToken is BasicToken {
      
        event Burn(address indexed burner, uint256 value);
      
        /**
         * @dev Burns a specific amount of tokens.
         * @param _value The amount of token to be burned.
         */
        function burn(uint256 _value) public {
          _burn(msg.sender, _value);
        }
      
        function _burn(address _who, uint256 _value) internal {
          require(_value <= balances[_who]);
          // no need to require value <= totalSupply, since that would imply the
          // sender's balance is greater than the totalSupply, which *should* be an assertion failure
      
          balances[_who] = balances[_who].sub(_value);
          totalSupply_ = totalSupply_.sub(_value);
          emit Burn(_who, _value);
          emit Transfer(_who, address(0), _value);
        }
      }
      
      // File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol
      
      /**
       * @title Pausable
       * @dev Base contract which allows children to implement an emergency stop mechanism.
       */
      contract Pausable is Ownable {
        event Pause();
        event Unpause();
      
        bool public paused = false;
      
      
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         */
        modifier whenNotPaused() {
          require(!paused);
          _;
        }
      
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         */
        modifier whenPaused() {
          require(paused);
          _;
        }
      
        /**
         * @dev called by the owner to pause, triggers stopped state
         */
        function pause() public onlyOwner whenNotPaused {
          paused = true;
          emit Pause();
        }
      
        /**
         * @dev called by the owner to unpause, returns to normal state
         */
        function unpause() public onlyOwner whenPaused {
          paused = false;
          emit Unpause();
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/PausableToken.sol
      
      /**
       * @title Pausable token
       * @dev StandardToken modified with pausable transfers.
       **/
      contract PausableToken is StandardToken, Pausable {
      
        function transfer(
          address _to,
          uint256 _value
        )
          public
          whenNotPaused
          returns (bool)
        {
          return super.transfer(_to, _value);
        }
      
        function transferFrom(
          address _from,
          address _to,
          uint256 _value
        )
          public
          whenNotPaused
          returns (bool)
        {
          return super.transferFrom(_from, _to, _value);
        }
      
        function approve(
          address _spender,
          uint256 _value
        )
          public
          whenNotPaused
          returns (bool)
        {
          return super.approve(_spender, _value);
        }
      
        function increaseApproval(
          address _spender,
          uint _addedValue
        )
          public
          whenNotPaused
          returns (bool success)
        {
          return super.increaseApproval(_spender, _addedValue);
        }
      
        function decreaseApproval(
          address _spender,
          uint _subtractedValue
        )
          public
          whenNotPaused
          returns (bool success)
        {
          return super.decreaseApproval(_spender, _subtractedValue);
        }
      }
      
      // File: openzeppelin-solidity/contracts/ownership/Claimable.sol
      
      /**
       * @title Claimable
       * @dev Extension for the Ownable contract, where the ownership needs to be claimed.
       * This allows the new owner to accept the transfer.
       */
      contract Claimable is Ownable {
        address public pendingOwner;
      
        /**
         * @dev Modifier throws if called by any account other than the pendingOwner.
         */
        modifier onlyPendingOwner() {
          require(msg.sender == pendingOwner);
          _;
        }
      
        /**
         * @dev Allows the current owner to set the pendingOwner address.
         * @param newOwner The address to transfer ownership to.
         */
        function transferOwnership(address newOwner) public onlyOwner {
          pendingOwner = newOwner;
        }
      
        /**
         * @dev Allows the pendingOwner address to finalize the transfer.
         */
        function claimOwnership() public onlyPendingOwner {
          emit OwnershipTransferred(owner, pendingOwner);
          owner = pendingOwner;
          pendingOwner = address(0);
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol
      
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure.
       * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
        function safeTransfer(
          ERC20Basic _token,
          address _to,
          uint256 _value
        )
          internal
        {
          require(_token.transfer(_to, _value));
        }
      
        function safeTransferFrom(
          ERC20 _token,
          address _from,
          address _to,
          uint256 _value
        )
          internal
        {
          require(_token.transferFrom(_from, _to, _value));
        }
      
        function safeApprove(
          ERC20 _token,
          address _spender,
          uint256 _value
        )
          internal
        {
          require(_token.approve(_spender, _value));
        }
      }
      
      // File: openzeppelin-solidity/contracts/ownership/CanReclaimToken.sol
      
      /**
       * @title Contracts that should be able to recover tokens
       * @author SylTi
       * @dev This allow a contract to recover any ERC20 token received in a contract by transferring the balance to the contract owner.
       * This will prevent any accidental loss of tokens.
       */
      contract CanReclaimToken is Ownable {
        using SafeERC20 for ERC20Basic;
      
        /**
         * @dev Reclaim all ERC20Basic compatible tokens
         * @param _token ERC20Basic The address of the token contract
         */
        function reclaimToken(ERC20Basic _token) external onlyOwner {
          uint256 balance = _token.balanceOf(this);
          _token.safeTransfer(owner, balance);
        }
      
      }
      
      // File: contracts/utils/OwnableContract.sol
      
      // empty block is used as this contract just inherits others.
      contract OwnableContract is CanReclaimToken, Claimable { } /* solhint-disable-line no-empty-blocks */
      
      // File: contracts/token/WBTC.sol
      
      contract WBTC is StandardToken, DetailedERC20("Wrapped BTC", "WBTC", 8),
          MintableToken, BurnableToken, PausableToken, OwnableContract {
      
          function burn(uint value) public onlyOwner {
              super.burn(value);
          }
      
          function finishMinting() public onlyOwner returns (bool) {
              return false;
          }
      
          function renounceOwnership() public onlyOwner {
              revert("renouncing ownership is blocked");
          }
      }

      File 3 of 4: CometWithExtendedAssetList
      // 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;
      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;
          /// @dev The storage slot for reentrancy guard flags
          bytes32 internal constant REENTRANCY_GUARD_FLAG_SLOT = bytes32(keccak256("comet.reentrancy.guard"));
          /// @dev The reentrancy guard statuses
          uint256 internal constant REENTRANCY_GUARD_NOT_ENTERED = 0;
          uint256 internal constant REENTRANCY_GUARD_ENTERED = 1;
          /**
           * @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;
      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 ReentrantCallBlocked();
          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 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;
          }
      }
      // 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;
      import "./CometMainInterface.sol";
      import "./IERC20NonStandard.sol";
      import "./IPriceFeed.sol";
      import "./IAssetListFactory.sol";
      import "./IAssetListFactoryHolder.sol";
      import "./IAssetList.sol";
      /**
       * @title Compound's Comet Contract
       * @notice An efficient monolithic money market protocol
       * @author Compound
       */
      contract CometWithExtendedAssetList 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;
          
          /// @notice The address of the asset list
          address immutable public assetList;
          uint8 internal constant MAX_ASSETS_FOR_ASSET_LIST = 24;
          /**
           * @notice Construct a new protocol instance
           * @param config The mapping of initial/constant parameters
           **/
          constructor(Configuration memory config) {
              // Sanity checks
              uint8 decimals_ = IERC20NonStandard(config.baseToken).decimals();
              if (decimals_ > MAX_BASE_DECIMALS) revert BadDecimals();
              if (config.storeFrontPriceFactor > FACTOR_SCALE) revert BadDiscount();
              if (config.assetConfigs.length > MAX_ASSETS_FOR_ASSET_LIST) 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);
              assetList = IAssetListFactory(IAssetListFactoryHolder(extensionDelegate).assetListFactory()).createAssetList(config.assetConfigs);
          }
          /**
           * @dev Prevents marked functions from being reentered 
           * Note: this restrict contracts from calling comet functions in their hooks.
           * Doing so will cause the transaction to revert.
           */
          modifier nonReentrant() {
              nonReentrantBefore();
              _;
              nonReentrantAfter();
          }
          /**
           * @dev Checks that the reentrancy flag is not set and then sets the flag
           */
          function nonReentrantBefore() internal {
              bytes32 slot = REENTRANCY_GUARD_FLAG_SLOT;
              uint256 status;
              assembly ("memory-safe") {
                  status := sload(slot)
              }
              if (status == REENTRANCY_GUARD_ENTERED) revert ReentrantCallBlocked();
              assembly ("memory-safe") {
                  sstore(slot, REENTRANCY_GUARD_ENTERED)
              }
          }
          /**
           * @dev Unsets the reentrancy flag
           */
          function nonReentrantAfter() internal {
              bytes32 slot = REENTRANCY_GUARD_FLAG_SLOT;
              uint256 status;
              assembly ("memory-safe") {
                  sstore(slot, REENTRANCY_GUARD_NOT_ENTERED)
              }
          }
          /**
           * @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;
          }
          /**
           * @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) {
              return IAssetList(assetList).getAssetInfo(i);
          }
          /**
           * @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 IERC20NonStandard(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 = IERC20NonStandard(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;
              uint8 _reserved = userBasic[account]._reserved;
              int liquidity = signedMulPrice(
                  presentValue(principal),
                  getPrice(baseTokenPriceFeed),
                  uint64(baseScale)
              );
              for (uint8 i = 0; i < numAssets; ) {
                  if (isInAsset(assetsIn, i, _reserved)) {
                      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;
              uint8 _reserved = userBasic[account]._reserved;
              int liquidity = signedMulPrice(
                  presentValue(principal),
                  getPrice(baseTokenPriceFeed),
                  uint64(baseScale)
              );
              for (uint8 i = 0; i < numAssets; ) {
                  if (isInAsset(assetsIn, i, _reserved)) {
                      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
           * @dev _reserved is used to check bits 16-23 of assetsIn
           */
          function isInAsset(uint16 assetsIn, uint8 assetOffset, uint8 _reserved) internal pure returns (bool) {
              if (assetOffset < 16) {
                  // check bit in assetsIn (for bits 0-15)
                  return (assetsIn & (uint16(1) << assetOffset)) != 0;
              } else if (assetOffset < 24) {
                  // check bit in reserved (for bits 16-23)
                  return (_reserved & (uint8(1) << (assetOffset - 16))) != 0;
              }
              return false; // if assetOffset >= 24 (should not happen)
          }
          /**
           * @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
                  if (assetInfo.offset < 16) {
                      // set bit in assetsIn for bits 0-15
                      userBasic[account].assetsIn |= (uint16(1) << assetInfo.offset);
                  } else if (assetInfo.offset < 24) {
                      // set bit in _reserved for bits 16-23
                      userBasic[account]._reserved |= (uint8(1) << (assetInfo.offset - 16));
                  }
              } else if (initialUserBalance != 0 && finalUserBalance == 0) {
                  // clear bit for asset
                  if (assetInfo.offset < 16) {
                      // clear bit in assetsIn for bits 0-15
                      userBasic[account].assetsIn &= ~(uint16(1) << assetInfo.offset);
                  } else if (assetInfo.offset < 24) {
                      // clear bit in _reserved for bits 16-23
                      userBasic[account]._reserved &= ~(uint8(1) << (assetInfo.offset - 16));
                  }
              }
          }
          /**
           * @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 and returns the final amount transferred (taking into account any fees)
           * @dev Note: Safely handles non-standard ERC-20 tokens that do not return a value. See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
           */
          function doTransferIn(address asset, address from, uint amount) internal returns (uint) {
              uint256 preTransferBalance = IERC20NonStandard(asset).balanceOf(address(this));
              IERC20NonStandard(asset).transferFrom(from, address(this), amount);
              bool success;
              assembly ("memory-safe") {
                  switch returndatasize()
                      case 0 {                       // This is a non-standard ERC-20
                          success := not(0)          // set success to true
                      }
                      case 32 {                      // This is a compliant ERC-20
                          returndatacopy(0, 0, 32)
                          success := mload(0)        // Set `success = returndata` of override external call
                      }
                      default {                      // This is an excessively non-compliant ERC-20, revert.
                          revert(0, 0)
                      }
              }
              if (!success) revert TransferInFailed();
              return IERC20NonStandard(asset).balanceOf(address(this)) - preTransferBalance;
          }
          /**
           * @dev Safe ERC20 transfer out
           * @dev Note: Safely handles non-standard ERC-20 tokens that do not return a value. See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
           */
          function doTransferOut(address asset, address to, uint amount) internal {
              IERC20NonStandard(asset).transfer(to, amount);
              bool success;
              assembly ("memory-safe") {
                  switch returndatasize()
                      case 0 {                       // This is a non-standard ERC-20
                          success := not(0)          // set success to true
                      }
                      case 32 {                      // This is a compliant ERC-20
                          returndatacopy(0, 0, 32)
                          success := mload(0)        // Set `success = returndata` of override external call
                      }
                      default {                      // This is an excessively non-compliant ERC-20, revert.
                          revert(0, 0)
                      }
              }
              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 nonReentrant {
              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 {
              amount = 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 {
              amount = safe128(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 nonReentrant {
              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 nonReentrant {
              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;
              uint8 _reserved = accountUser._reserved;
              uint256 basePrice = getPrice(baseTokenPriceFeed);
              uint256 deltaValue = 0;
              for (uint8 i = 0; i < numAssets; ) {
                  if (isInAsset(assetsIn, i, _reserved)) {
                      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;
              userBasic[account]._reserved = 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 nonReentrant {
              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.
              baseAmount = 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
           * @dev Note: For USDT, if there is non-zero prior allowance, it must be reset to 0 first before setting a new value in proposal
           * @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();
              IERC20NonStandard(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 Asset List
       * @author Compound
       */
      interface IAssetList {
          function getAssetInfo(uint8 i) external view returns (CometCore.AssetInfo memory);
          function numAssets() external view returns (uint8);
      }// SPDX-License-Identifier: BUSL-1.1
      pragma solidity 0.8.15;
      import "./CometCore.sol";
      /**
       * @title Compound's Asset List Factory
       * @author Compound
       */
      interface IAssetListFactory {
          /**
           * @notice Create a new asset list
           * @param assetConfigs The asset configurations
           * @return assetList The address of the new asset list
           */
          function createAssetList(CometCore.AssetConfig[] memory assetConfigs) external returns (address assetList);
      }// SPDX-License-Identifier: BUSL-1.1
      pragma solidity 0.8.15;
      /**
       * @title Compound's Asset List Factory Holder Interface
       * @author Compound
       */
      interface IAssetListFactoryHolder {
          /**
           * @notice Get the asset list factory
           * @return assetListFactory The asset list factory address
           */
          function assetListFactory() external view returns (address);
      }// SPDX-License-Identifier: BUSL-1.1
      pragma solidity 0.8.15;
      /**
       * @title IERC20NonStandard
       * @dev Version of ERC20 with no return values for `approve`, `transfer`, and `transferFrom`
       *  See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
       */
      interface IERC20NonStandard {
          function name() external view returns (string memory);
          function symbol() external view returns (string memory);
          function decimals() external view returns (uint8);
          /**
           * @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)
           */
          function approve(address spender, uint256 amount) external;
          /**
           * @notice Transfer `value` tokens from `msg.sender` to `to`
           * @param to The address of the destination account
           * @param value The number of tokens to transfer
           */
          function transfer(address to, uint256 value) external;
          /**
           * @notice Transfer `value` tokens from `from` to `to`
           * @param from The address of the source account
           * @param to The address of the destination account
           * @param value The number of tokens to transfer
           */
          function transferFrom(address from, address to, uint256 value) external;
          /**
           * @notice Gets the balance of the specified address
           * @param account The address from which the balance will be retrieved
           */
          function balanceOf(address account) external view returns (uint256);
      }
      // 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
          );
      }

      File 4 of 4: AssetList
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity 0.8.15;
      import "./IPriceFeed.sol";
      import "./IERC20NonStandard.sol";
      import "./CometMainInterface.sol";
      import "./CometCore.sol";
      /**
       * @title Compound's Asset List
       * @author Compound
       */
      contract AssetList {
          /// @dev The decimals required for a price feed
          uint8 internal constant PRICE_FEED_DECIMALS = 8;
          /// @dev The scale for factors
          uint64 internal constant FACTOR_SCALE = 1e18;
          /// @dev The max value for a collateral factor (1)
          uint64 internal constant MAX_COLLATERAL_FACTOR = FACTOR_SCALE;
          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;
          uint256 internal immutable asset15_a;
          uint256 internal immutable asset15_b;
          uint256 internal immutable asset16_a;
          uint256 internal immutable asset16_b;
          uint256 internal immutable asset17_a;
          uint256 internal immutable asset17_b;
          uint256 internal immutable asset18_a;
          uint256 internal immutable asset18_b;
          uint256 internal immutable asset19_a;
          uint256 internal immutable asset19_b;
          uint256 internal immutable asset20_a;
          uint256 internal immutable asset20_b;
          uint256 internal immutable asset21_a;
          uint256 internal immutable asset21_b;
          uint256 internal immutable asset22_a;
          uint256 internal immutable asset22_b;
          uint256 internal immutable asset23_a;
          uint256 internal immutable asset23_b;
          /// @notice The number of assets this contract actually supports
          uint8 public immutable numAssets;
          
          constructor(CometConfiguration.AssetConfig[] memory assetConfigs) {
              uint8 _numAssets = uint8(assetConfigs.length);
              numAssets = _numAssets;
              
              (asset00_a, asset00_b) = getPackedAssetInternal(assetConfigs, 0);
              (asset01_a, asset01_b) = getPackedAssetInternal(assetConfigs, 1);
              (asset02_a, asset02_b) = getPackedAssetInternal(assetConfigs, 2);
              (asset03_a, asset03_b) = getPackedAssetInternal(assetConfigs, 3);
              (asset04_a, asset04_b) = getPackedAssetInternal(assetConfigs, 4);
              (asset05_a, asset05_b) = getPackedAssetInternal(assetConfigs, 5);
              (asset06_a, asset06_b) = getPackedAssetInternal(assetConfigs, 6);
              (asset07_a, asset07_b) = getPackedAssetInternal(assetConfigs, 7);
              (asset08_a, asset08_b) = getPackedAssetInternal(assetConfigs, 8);
              (asset09_a, asset09_b) = getPackedAssetInternal(assetConfigs, 9);
              (asset10_a, asset10_b) = getPackedAssetInternal(assetConfigs, 10);
              (asset11_a, asset11_b) = getPackedAssetInternal(assetConfigs, 11);
              (asset12_a, asset12_b) = getPackedAssetInternal(assetConfigs, 12);
              (asset13_a, asset13_b) = getPackedAssetInternal(assetConfigs, 13);
              (asset14_a, asset14_b) = getPackedAssetInternal(assetConfigs, 14);
              (asset15_a, asset15_b) = getPackedAssetInternal(assetConfigs, 15);
              (asset16_a, asset16_b) = getPackedAssetInternal(assetConfigs, 16);
              (asset17_a, asset17_b) = getPackedAssetInternal(assetConfigs, 17);
              (asset18_a, asset18_b) = getPackedAssetInternal(assetConfigs, 18);
              (asset19_a, asset19_b) = getPackedAssetInternal(assetConfigs, 19);
              (asset20_a, asset20_b) = getPackedAssetInternal(assetConfigs, 20);
              (asset21_a, asset21_b) = getPackedAssetInternal(assetConfigs, 21);
              (asset22_a, asset22_b) = getPackedAssetInternal(assetConfigs, 22);
              (asset23_a, asset23_b) = getPackedAssetInternal(assetConfigs, 23);
          }
          /**
           * @dev Checks and gets the packed asset info for storage in 2 variables
           * - in first variable, the asset address is stored in the lower 160 bits (address can be interpreted as uint160),
           *      the borrow collateral factor in the next 16 bits,
           *      the liquidate collateral factor in the next 16 bits,
           *      and the liquidation factor in the next 16 bits
           * - in the second variable, the price feed address is stored in the lower 160 bits,
           *      the asset decimals in the next 8 bits,
           *      and the supply cap in the next 64 bits
           * @param assetConfigs The asset configurations
           * @param i The index of the asset info to get
           * @return The packed asset info
           */
          function getPackedAssetInternal(CometConfiguration.AssetConfig[] memory assetConfigs, uint i) internal view returns (uint256, uint256) {
              CometConfiguration.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 CometMainInterface.BadDecimals();
              if (IERC20NonStandard(asset).decimals() != decimals_) revert CometMainInterface.BadDecimals();
              // Ensure collateral factors are within range
              if (assetConfig.borrowCollateralFactor >= assetConfig.liquidateCollateralFactor) revert CometMainInterface.BorrowCFTooLarge();
              if (assetConfig.liquidateCollateralFactor > MAX_COLLATERAL_FACTOR) revert CometMainInterface.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 CometMainInterface.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) public view returns (CometCore.AssetInfo memory) {
              if (i >= numAssets) revert CometMainInterface.BadAsset();
              uint256 word_a;
              uint256 word_b;
              if(i == 0){
                  word_a = asset00_a;
                  word_b = asset00_b;
              }
              if(i == 1){
                  word_a = asset01_a;
                  word_b = asset01_b;
              }
              if(i == 2){
                  word_a = asset02_a;
                  word_b = asset02_b;
              }
              if(i == 3){
                  word_a = asset03_a;
                  word_b = asset03_b;
              }
              if(i == 4){
                  word_a = asset04_a;
                  word_b = asset04_b;
              }
              if(i == 5){
                  word_a = asset05_a;
                  word_b = asset05_b;
              }
              if(i == 6){
                  word_a = asset06_a;
                  word_b = asset06_b;
              }
              if(i == 7){
                  word_a = asset07_a;
                  word_b = asset07_b;
              }
              if(i == 8){
                  word_a = asset08_a;
                  word_b = asset08_b;
              }
              if(i == 9){
                  word_a = asset09_a;
                  word_b = asset09_b;
              }
              if(i == 10){
                  word_a = asset10_a;
                  word_b = asset10_b;
              }
              if(i == 11){
                  word_a = asset11_a;
                  word_b = asset11_b;
              }
              if(i == 12){
                  word_a = asset12_a;
                  word_b = asset12_b;
              }
              if(i == 13){
                  word_a = asset13_a;
                  word_b = asset13_b;
              }
              if(i == 14){
                  word_a = asset14_a;
                  word_b = asset14_b;
              }
              if(i == 15){
                  word_a = asset15_a;
                  word_b = asset15_b;
              }
              if(i == 16){
                  word_a = asset16_a;
                  word_b = asset16_b;
              }
              if(i == 17){
                  word_a = asset17_a;
                  word_b = asset17_b;
              }
              if(i == 18){
                  word_a = asset18_a;
                  word_b = asset18_b;
              }
              if(i == 19){
                  word_a = asset19_a;
                  word_b = asset19_b;
              }
              if(i == 20){
                  word_a = asset20_a;
                  word_b = asset20_b;
              }
              if(i == 21){
                  word_a = asset21_a;
                  word_b = asset21_b;
              }
              if(i == 22){
                  word_a = asset22_a;
                  word_b = asset22_b;
              }
              if(i == 23){
                  word_a = asset23_a;
                  word_b = asset23_b;
              }
              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 CometCore.AssetInfo({
                  offset: i,
                  asset: asset,
                  priceFeed: priceFeed,
                  scale: scale,
                  borrowCollateralFactor: borrowCollateralFactor,
                  liquidateCollateralFactor: liquidateCollateralFactor,
                  liquidationFactor: liquidationFactor,
                  supplyCap: supplyCap
               });
          }
      }// 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;
      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;
          /// @dev The storage slot for reentrancy guard flags
          bytes32 internal constant REENTRANCY_GUARD_FLAG_SLOT = bytes32(keccak256("comet.reentrancy.guard"));
          /// @dev The reentrancy guard statuses
          uint256 internal constant REENTRANCY_GUARD_NOT_ENTERED = 0;
          uint256 internal constant REENTRANCY_GUARD_ENTERED = 1;
          /**
           * @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;
      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 ReentrantCallBlocked();
          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 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;
          }
      }
      // 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 IERC20NonStandard
       * @dev Version of ERC20 with no return values for `approve`, `transfer`, and `transferFrom`
       *  See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
       */
      interface IERC20NonStandard {
          function name() external view returns (string memory);
          function symbol() external view returns (string memory);
          function decimals() external view returns (uint8);
          /**
           * @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)
           */
          function approve(address spender, uint256 amount) external;
          /**
           * @notice Transfer `value` tokens from `msg.sender` to `to`
           * @param to The address of the destination account
           * @param value The number of tokens to transfer
           */
          function transfer(address to, uint256 value) external;
          /**
           * @notice Transfer `value` tokens from `from` to `to`
           * @param from The address of the source account
           * @param to The address of the destination account
           * @param value The number of tokens to transfer
           */
          function transferFrom(address from, address to, uint256 value) external;
          /**
           * @notice Gets the balance of the specified address
           * @param account The address from which the balance will be retrieved
           */
          function balanceOf(address account) external view returns (uint256);
      }
      // 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
          );
      }