{ "language": "Solidity", "sources": { "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n function __Ownable_init() internal onlyInitializing {\n __Ownable_init_unchained();\n }\n\n function __Ownable_init_unchained() internal onlyInitializing {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" }, "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n * @custom:oz-retyped-from bool\n */\n uint8 private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint8 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts.\n *\n * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n * constructor.\n *\n * Emits an {Initialized} event.\n */\n modifier initializer() {\n bool isTopLevelCall = !_initializing;\n require(\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n \"Initializable: contract is already initialized\"\n );\n _initialized = 1;\n if (isTopLevelCall) {\n _initializing = true;\n }\n _;\n if (isTopLevelCall) {\n _initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n * are added through upgrades and that require initialization.\n *\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n * cannot be nested. If one is invoked in the context of another, execution will revert.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n *\n * WARNING: setting the version to 255 will prevent any future reinitialization.\n *\n * Emits an {Initialized} event.\n */\n modifier reinitializer(uint8 version) {\n require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n _initialized = version;\n _initializing = true;\n _;\n _initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n *\n * Emits an {Initialized} event the first time it is successfully executed.\n */\n function _disableInitializers() internal virtual {\n require(!_initializing, \"Initializable: contract is initializing\");\n if (_initialized < type(uint8).max) {\n _initialized = type(uint8).max;\n emit Initialized(type(uint8).max);\n }\n }\n\n /**\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\n */\n function _getInitializedVersion() internal view returns (uint8) {\n return _initialized;\n }\n\n /**\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n */\n function _isInitializing() internal view returns (bool) {\n return _initializing;\n }\n}\n" }, "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n" }, "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal onlyInitializing {\n }\n\n function __Context_init_unchained() internal onlyInitializing {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n" }, "@openzeppelin/contracts/interfaces/draft-IERC1822.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\n * proxy whose upgrades are fully controlled by the current implementation.\n */\ninterface IERC1822Proxiable {\n /**\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\n * address.\n *\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n * function revert if invoked through a proxy.\n */\n function proxiableUUID() external view returns (bytes32);\n}\n" }, "@openzeppelin/contracts/interfaces/IERC20.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../token/ERC20/IERC20.sol\";\n" }, "@openzeppelin/contracts/interfaces/IERC4626.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../token/ERC20/IERC20.sol\";\nimport \"../token/ERC20/extensions/IERC20Metadata.sol\";\n\n/**\n * @dev Interface of the ERC4626 \"Tokenized Vault Standard\", as defined in\n * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].\n *\n * _Available since v4.7._\n */\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed sender,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}\n" }, "@openzeppelin/contracts/interfaces/IERC721.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../token/ERC721/IERC721.sol\";\n" }, "@openzeppelin/contracts/proxy/beacon/IBeacon.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\n */\ninterface IBeacon {\n /**\n * @dev Must return an address that can be used as a delegate call target.\n *\n * {BeaconProxy} will check that this address is a contract.\n */\n function implementation() external view returns (address);\n}\n" }, "@openzeppelin/contracts/proxy/Clones.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for\n * deploying minimal proxy contracts, also known as \"clones\".\n *\n * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies\n * > a minimal bytecode implementation that delegates all calls to a known, fixed address.\n *\n * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`\n * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the\n * deterministic method.\n *\n * _Available since v3.4._\n */\nlibrary Clones {\n /**\n * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n *\n * This function uses the create opcode, which should never revert.\n */\n function clone(address implementation) internal returns (address instance) {\n /// @solidity memory-safe-assembly\n assembly {\n // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes\n // of the `implementation` address with the bytecode before the address.\n mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\n // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.\n mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))\n instance := create(0, 0x09, 0x37)\n }\n require(instance != address(0), \"ERC1167: create failed\");\n }\n\n /**\n * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n *\n * This function uses the create2 opcode and a `salt` to deterministically deploy\n * the clone. Using the same `implementation` and `salt` multiple time will revert, since\n * the clones cannot be deployed twice at the same address.\n */\n function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {\n /// @solidity memory-safe-assembly\n assembly {\n // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes\n // of the `implementation` address with the bytecode before the address.\n mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\n // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.\n mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))\n instance := create2(0, 0x09, 0x37, salt)\n }\n require(instance != address(0), \"ERC1167: create2 failed\");\n }\n\n /**\n * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n */\n function predictDeterministicAddress(\n address implementation,\n bytes32 salt,\n address deployer\n ) internal pure returns (address predicted) {\n /// @solidity memory-safe-assembly\n assembly {\n let ptr := mload(0x40)\n mstore(add(ptr, 0x38), deployer)\n mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)\n mstore(add(ptr, 0x14), implementation)\n mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)\n mstore(add(ptr, 0x58), salt)\n mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))\n predicted := keccak256(add(ptr, 0x43), 0x55)\n }\n }\n\n /**\n * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n */\n function predictDeterministicAddress(address implementation, bytes32 salt)\n internal\n view\n returns (address predicted)\n {\n return predictDeterministicAddress(implementation, salt, address(this));\n }\n}\n" }, "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Proxy.sol\";\nimport \"./ERC1967Upgrade.sol\";\n\n/**\n * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an\n * implementation address that can be changed. This address is stored in storage in the location specified by\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the\n * implementation behind the proxy.\n */\ncontract ERC1967Proxy is Proxy, ERC1967Upgrade {\n /**\n * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.\n *\n * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded\n * function call, and allows initializing the storage of the proxy like a Solidity constructor.\n */\n constructor(address _logic, bytes memory _data) payable {\n _upgradeToAndCall(_logic, _data, false);\n }\n\n /**\n * @dev Returns the current implementation address.\n */\n function _implementation() internal view virtual override returns (address impl) {\n return ERC1967Upgrade._getImplementation();\n }\n}\n" }, "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../beacon/IBeacon.sol\";\nimport \"../../interfaces/draft-IERC1822.sol\";\nimport \"../../utils/Address.sol\";\nimport \"../../utils/StorageSlot.sol\";\n\n/**\n * @dev This abstract contract provides getters and event emitting update functions for\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\n *\n * _Available since v4.1._\n *\n * @custom:oz-upgrades-unsafe-allow delegatecall\n */\nabstract contract ERC1967Upgrade {\n // This is the keccak-256 hash of \"eip1967.proxy.rollback\" subtracted by 1\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Emitted when the implementation is upgraded.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Returns the current implementation address.\n */\n function _getImplementation() internal view returns (address) {\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 implementation slot.\n */\n function _setImplementation(address newImplementation) private {\n require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n }\n\n /**\n * @dev Perform implementation upgrade\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Perform implementation upgrade with additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCall(\n address newImplementation,\n bytes memory data,\n bool forceCall\n ) internal {\n _upgradeTo(newImplementation);\n if (data.length > 0 || forceCall) {\n Address.functionDelegateCall(newImplementation, data);\n }\n }\n\n /**\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCallUUPS(\n address newImplementation,\n bytes memory data,\n bool forceCall\n ) internal {\n // Upgrades from old implementations will perform a rollback test. This test requires the new\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\n // this special case will break upgrade paths from old UUPS implementation to new ones.\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\n _setImplementation(newImplementation);\n } else {\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\n require(slot == _IMPLEMENTATION_SLOT, \"ERC1967Upgrade: unsupported proxiableUUID\");\n } catch {\n revert(\"ERC1967Upgrade: new implementation is not UUPS\");\n }\n _upgradeToAndCall(newImplementation, data, forceCall);\n }\n }\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Emitted when the admin account has changed.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Returns the current admin.\n */\n function _getAdmin() internal view returns (address) {\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 admin slot.\n */\n function _setAdmin(address newAdmin) private {\n require(newAdmin != address(0), \"ERC1967: new admin is the zero address\");\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\n }\n\n /**\n * @dev Changes the admin of the proxy.\n *\n * Emits an {AdminChanged} event.\n */\n function _changeAdmin(address newAdmin) internal {\n emit AdminChanged(_getAdmin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\n */\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\n\n /**\n * @dev Emitted when the beacon is upgraded.\n */\n event BeaconUpgraded(address indexed beacon);\n\n /**\n * @dev Returns the current beacon.\n */\n function _getBeacon() internal view returns (address) {\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\n }\n\n /**\n * @dev Stores a new beacon in the EIP1967 beacon slot.\n */\n function _setBeacon(address newBeacon) private {\n require(Address.isContract(newBeacon), \"ERC1967: new beacon is not a contract\");\n require(\n Address.isContract(IBeacon(newBeacon).implementation()),\n \"ERC1967: beacon implementation is not a contract\"\n );\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\n }\n\n /**\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\n *\n * Emits a {BeaconUpgraded} event.\n */\n function _upgradeBeaconToAndCall(\n address newBeacon,\n bytes memory data,\n bool forceCall\n ) internal {\n _setBeacon(newBeacon);\n emit BeaconUpgraded(newBeacon);\n if (data.length > 0 || forceCall) {\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\n }\n }\n}\n" }, "@openzeppelin/contracts/proxy/Proxy.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\n * be specified by overriding the virtual {_implementation} function.\n *\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\n * different contract through the {_delegate} function.\n *\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\n */\nabstract contract Proxy {\n /**\n * @dev Delegates the current call to `implementation`.\n *\n * This function does not return to its internal call site, it will return directly to the external caller.\n */\n function _delegate(address implementation) internal virtual {\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function\n * and {_fallback} should delegate.\n */\n function _implementation() internal view virtual returns (address);\n\n /**\n * @dev Delegates the current call to the address returned by `_implementation()`.\n *\n * This function does not return to its internal call site, it will return directly to the external caller.\n */\n function _fallback() internal virtual {\n _beforeFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\n * function in the contract matches the call data.\n */\n fallback() external payable virtual {\n _fallback();\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\n * is empty.\n */\n receive() external payable virtual {\n _fallback();\n }\n\n /**\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\n * call, or as part of the Solidity `fallback` or `receive` functions.\n *\n * If overridden should call `super._beforeFallback()`.\n */\n function _beforeFallback() internal virtual {}\n}\n" }, "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../interfaces/draft-IERC1822.sol\";\nimport \"../ERC1967/ERC1967Upgrade.sol\";\n\n/**\n * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an\n * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.\n *\n * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is\n * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing\n * `UUPSUpgradeable` with a custom implementation of upgrades.\n *\n * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.\n *\n * _Available since v4.1._\n */\nabstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment\n address private immutable __self = address(this);\n\n /**\n * @dev Check that the execution is being performed through a delegatecall call and that the execution context is\n * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case\n * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a\n * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to\n * fail.\n */\n modifier onlyProxy() {\n require(address(this) != __self, \"Function must be called through delegatecall\");\n require(_getImplementation() == __self, \"Function must be called through active proxy\");\n _;\n }\n\n /**\n * @dev Check that the execution is not being performed through a delegate call. This allows a function to be\n * callable on the implementing contract but not through proxies.\n */\n modifier notDelegated() {\n require(address(this) == __self, \"UUPSUpgradeable: must not be called through delegatecall\");\n _;\n }\n\n /**\n * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the\n * implementation. It is used to validate the implementation's compatibility when performing an upgrade.\n *\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.\n */\n function proxiableUUID() external view virtual override notDelegated returns (bytes32) {\n return _IMPLEMENTATION_SLOT;\n }\n\n /**\n * @dev Upgrade the implementation of the proxy to `newImplementation`.\n *\n * Calls {_authorizeUpgrade}.\n *\n * Emits an {Upgraded} event.\n */\n function upgradeTo(address newImplementation) external virtual onlyProxy {\n _authorizeUpgrade(newImplementation);\n _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);\n }\n\n /**\n * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call\n * encoded in `data`.\n *\n * Calls {_authorizeUpgrade}.\n *\n * Emits an {Upgraded} event.\n */\n function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {\n _authorizeUpgrade(newImplementation);\n _upgradeToAndCallUUPS(newImplementation, data, true);\n }\n\n /**\n * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by\n * {upgradeTo} and {upgradeToAndCall}.\n *\n * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.\n *\n * ```solidity\n * function _authorizeUpgrade(address) internal override onlyOwner {}\n * ```\n */\n function _authorizeUpgrade(address newImplementation) internal virtual;\n}\n" }, "@openzeppelin/contracts/security/Pausable.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n _requireNotPaused();\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n _requirePaused();\n _;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Throws if the contract is paused.\n */\n function _requireNotPaused() internal view virtual {\n require(!paused(), \"Pausable: paused\");\n }\n\n /**\n * @dev Throws if the contract is not paused.\n */\n function _requirePaused() internal view virtual {\n require(paused(), \"Pausable: not paused\");\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" }, "@openzeppelin/contracts/security/ReentrancyGuard.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor() {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be _NOT_ENTERED\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n}\n" }, "@openzeppelin/contracts/token/ERC20/ERC20.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _transfer(owner, to, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n * `transferFrom`. This is semantically equivalent to an infinite approval.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * NOTE: Does not update the allowance if the current allowance\n * is the maximum `uint256`.\n *\n * Requirements:\n *\n * - `from` and `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n * - the caller must have allowance for ``from``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual override returns (bool) {\n address spender = _msgSender();\n _spendAllowance(from, spender, amount);\n _transfer(from, to, amount);\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, allowance(owner, spender) + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n address owner = _msgSender();\n uint256 currentAllowance = allowance(owner, spender);\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(owner, spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `from` to `to`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n */\n function _transfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {\n require(from != address(0), \"ERC20: transfer from the zero address\");\n require(to != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, amount);\n\n uint256 fromBalance = _balances[from];\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[from] = fromBalance - amount;\n // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n // decrementing then incrementing.\n _balances[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n _afterTokenTransfer(from, to, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n unchecked {\n // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n _balances[account] += amount;\n }\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n // Overflow not possible: amount <= accountBalance <= totalSupply.\n _totalSupply -= amount;\n }\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n *\n * Does not update the allowance amount in case of infinite allowance.\n * Revert if not enough allowance is available.\n *\n * Might emit an {Approval} event.\n */\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" }, "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./draft-IERC20Permit.sol\";\nimport \"../ERC20.sol\";\nimport \"../../../utils/cryptography/ECDSA.sol\";\nimport \"../../../utils/cryptography/EIP712.sol\";\nimport \"../../../utils/Counters.sol\";\n\n/**\n * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * _Available since v3.4._\n */\nabstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {\n using Counters for Counters.Counter;\n\n mapping(address => Counters.Counter) private _nonces;\n\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private constant _PERMIT_TYPEHASH =\n keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n /**\n * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.\n * However, to ensure consistency with the upgradeable transpiler, we will continue\n * to reserve a slot.\n * @custom:oz-renamed-from _PERMIT_TYPEHASH\n */\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;\n\n /**\n * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `\"1\"`.\n *\n * It's a good idea to use the same `name` that is defined as the ERC20 token name.\n */\n constructor(string memory name) EIP712(name, \"1\") {}\n\n /**\n * @dev See {IERC20Permit-permit}.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual override {\n require(block.timestamp <= deadline, \"ERC20Permit: expired deadline\");\n\n bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));\n\n bytes32 hash = _hashTypedDataV4(structHash);\n\n address signer = ECDSA.recover(hash, v, r, s);\n require(signer == owner, \"ERC20Permit: invalid signature\");\n\n _approve(owner, spender, value);\n }\n\n /**\n * @dev See {IERC20Permit-nonces}.\n */\n function nonces(address owner) public view virtual override returns (uint256) {\n return _nonces[owner].current();\n }\n\n /**\n * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view override returns (bytes32) {\n return _domainSeparatorV4();\n }\n\n /**\n * @dev \"Consume a nonce\": return the current value and increment.\n *\n * _Available since v4.1._\n */\n function _useNonce(address owner) internal virtual returns (uint256 current) {\n Counters.Counter storage nonce = _nonces[owner];\n current = nonce.current();\n nonce.increment();\n }\n}\n" }, "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" }, "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" }, "@openzeppelin/contracts/token/ERC20/IERC20.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n" }, "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" }, "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721Enumerable is IERC721 {\n /**\n * @dev Returns the total amount of tokens stored by the contract.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns a token ID owned by `owner` at a given `index` of its token list.\n * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.\n */\n function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);\n\n /**\n * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.\n * Use along with {totalSupply} to enumerate all tokens.\n */\n function tokenByIndex(uint256 index) external view returns (uint256);\n}\n" }, "@openzeppelin/contracts/token/ERC721/IERC721.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721\n * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n * understand this adds an external call which potentially creates a reentrancy vulnerability.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n" }, "@openzeppelin/contracts/utils/Address.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n" }, "@openzeppelin/contracts/utils/Context.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" }, "@openzeppelin/contracts/utils/Counters.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title Counters\n * @author Matt Condon (@shrugs)\n * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number\n * of elements in a mapping, issuing ERC721 ids, or counting request ids.\n *\n * Include with `using Counters for Counters.Counter;`\n */\nlibrary Counters {\n struct Counter {\n // This variable should never be directly accessed by users of the library: interactions must be restricted to\n // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add\n // this feature: see https://github.com/ethereum/solidity/issues/4637\n uint256 _value; // default: 0\n }\n\n function current(Counter storage counter) internal view returns (uint256) {\n return counter._value;\n }\n\n function increment(Counter storage counter) internal {\n unchecked {\n counter._value += 1;\n }\n }\n\n function decrement(Counter storage counter) internal {\n uint256 value = counter._value;\n require(value > 0, \"Counter: decrement overflow\");\n unchecked {\n counter._value = value - 1;\n }\n }\n\n function reset(Counter storage counter) internal {\n counter._value = 0;\n }\n}\n" }, "@openzeppelin/contracts/utils/cryptography/ECDSA.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Strings.sol\";\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV // Deprecated in v4.8\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n /// @solidity memory-safe-assembly\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n" }, "@openzeppelin/contracts/utils/cryptography/EIP712.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./ECDSA.sol\";\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.\n *\n * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,\n * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding\n * they need in their contracts using a combination of `abi.encode` and `keccak256`.\n *\n * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding\n * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA\n * ({_hashTypedDataV4}).\n *\n * The implementation of the domain separator was designed to be as efficient as possible while still properly updating\n * the chain id to protect against replay attacks on an eventual fork of the chain.\n *\n * NOTE: This contract implements the version of the encoding known as \"v4\", as implemented by the JSON RPC method\n * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].\n *\n * _Available since v3.4._\n */\nabstract contract EIP712 {\n /* solhint-disable var-name-mixedcase */\n // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to\n // invalidate the cached domain separator if the chain id changes.\n bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;\n uint256 private immutable _CACHED_CHAIN_ID;\n address private immutable _CACHED_THIS;\n\n bytes32 private immutable _HASHED_NAME;\n bytes32 private immutable _HASHED_VERSION;\n bytes32 private immutable _TYPE_HASH;\n\n /* solhint-enable var-name-mixedcase */\n\n /**\n * @dev Initializes the domain separator and parameter caches.\n *\n * The meaning of `name` and `version` is specified in\n * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:\n *\n * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.\n * - `version`: the current major version of the signing domain.\n *\n * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart\n * contract upgrade].\n */\n constructor(string memory name, string memory version) {\n bytes32 hashedName = keccak256(bytes(name));\n bytes32 hashedVersion = keccak256(bytes(version));\n bytes32 typeHash = keccak256(\n \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"\n );\n _HASHED_NAME = hashedName;\n _HASHED_VERSION = hashedVersion;\n _CACHED_CHAIN_ID = block.chainid;\n _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);\n _CACHED_THIS = address(this);\n _TYPE_HASH = typeHash;\n }\n\n /**\n * @dev Returns the domain separator for the current chain.\n */\n function _domainSeparatorV4() internal view returns (bytes32) {\n if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {\n return _CACHED_DOMAIN_SEPARATOR;\n } else {\n return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);\n }\n }\n\n function _buildDomainSeparator(\n bytes32 typeHash,\n bytes32 nameHash,\n bytes32 versionHash\n ) private view returns (bytes32) {\n return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));\n }\n\n /**\n * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this\n * function returns the hash of the fully encoded EIP712 message for this domain.\n *\n * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:\n *\n * ```solidity\n * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(\n * keccak256(\"Mail(address to,string contents)\"),\n * mailTo,\n * keccak256(bytes(mailContents))\n * )));\n * address signer = ECDSA.recover(digest, signature);\n * ```\n */\n function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {\n return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);\n }\n}\n" }, "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary MerkleProof {\n /**\n * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree\n * defined by `root`. For this, a `proof` must be provided, containing\n * sibling hashes on the branch from the leaf to the root of the tree. Each\n * pair of leaves and each pair of pre-images are assumed to be sorted.\n */\n function verify(\n bytes32[] memory proof,\n bytes32 root,\n bytes32 leaf\n ) internal pure returns (bool) {\n return processProof(proof, leaf) == root;\n }\n\n /**\n * @dev Calldata version of {verify}\n *\n * _Available since v4.7._\n */\n function verifyCalldata(\n bytes32[] calldata proof,\n bytes32 root,\n bytes32 leaf\n ) internal pure returns (bool) {\n return processProofCalldata(proof, leaf) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. When processing the proof, the pairs\n * of leafs & pre-images are assumed to be sorted.\n *\n * _Available since v4.4._\n */\n function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {\n bytes32 computedHash = leaf;\n for (uint256 i = 0; i < proof.length; i++) {\n computedHash = _hashPair(computedHash, proof[i]);\n }\n return computedHash;\n }\n\n /**\n * @dev Calldata version of {processProof}\n *\n * _Available since v4.7._\n */\n function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {\n bytes32 computedHash = leaf;\n for (uint256 i = 0; i < proof.length; i++) {\n computedHash = _hashPair(computedHash, proof[i]);\n }\n return computedHash;\n }\n\n /**\n * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by\n * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.\n *\n * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.\n *\n * _Available since v4.7._\n */\n function multiProofVerify(\n bytes32[] memory proof,\n bool[] memory proofFlags,\n bytes32 root,\n bytes32[] memory leaves\n ) internal pure returns (bool) {\n return processMultiProof(proof, proofFlags, leaves) == root;\n }\n\n /**\n * @dev Calldata version of {multiProofVerify}\n *\n * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.\n *\n * _Available since v4.7._\n */\n function multiProofVerifyCalldata(\n bytes32[] calldata proof,\n bool[] calldata proofFlags,\n bytes32 root,\n bytes32[] memory leaves\n ) internal pure returns (bool) {\n return processMultiProofCalldata(proof, proofFlags, leaves) == root;\n }\n\n /**\n * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction\n * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another\n * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false\n * respectively.\n *\n * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree\n * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the\n * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).\n *\n * _Available since v4.7._\n */\n function processMultiProof(\n bytes32[] memory proof,\n bool[] memory proofFlags,\n bytes32[] memory leaves\n ) internal pure returns (bytes32 merkleRoot) {\n // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by\n // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the\n // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of\n // the merkle tree.\n uint256 leavesLen = leaves.length;\n uint256 totalHashes = proofFlags.length;\n\n // Check proof validity.\n require(leavesLen + proof.length - 1 == totalHashes, \"MerkleProof: invalid multiproof\");\n\n // The xxxPos values are \"pointers\" to the next value to consume in each array. All accesses are done using\n // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's \"pop\".\n bytes32[] memory hashes = new bytes32[](totalHashes);\n uint256 leafPos = 0;\n uint256 hashPos = 0;\n uint256 proofPos = 0;\n // At each step, we compute the next hash using two values:\n // - a value from the \"main queue\". If not all leaves have been consumed, we get the next leaf, otherwise we\n // get the next hash.\n // - depending on the flag, either another value for the \"main queue\" (merging branches) or an element from the\n // `proof` array.\n for (uint256 i = 0; i < totalHashes; i++) {\n bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];\n bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];\n hashes[i] = _hashPair(a, b);\n }\n\n if (totalHashes > 0) {\n return hashes[totalHashes - 1];\n } else if (leavesLen > 0) {\n return leaves[0];\n } else {\n return proof[0];\n }\n }\n\n /**\n * @dev Calldata version of {processMultiProof}.\n *\n * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.\n *\n * _Available since v4.7._\n */\n function processMultiProofCalldata(\n bytes32[] calldata proof,\n bool[] calldata proofFlags,\n bytes32[] memory leaves\n ) internal pure returns (bytes32 merkleRoot) {\n // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by\n // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the\n // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of\n // the merkle tree.\n uint256 leavesLen = leaves.length;\n uint256 totalHashes = proofFlags.length;\n\n // Check proof validity.\n require(leavesLen + proof.length - 1 == totalHashes, \"MerkleProof: invalid multiproof\");\n\n // The xxxPos values are \"pointers\" to the next value to consume in each array. All accesses are done using\n // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's \"pop\".\n bytes32[] memory hashes = new bytes32[](totalHashes);\n uint256 leafPos = 0;\n uint256 hashPos = 0;\n uint256 proofPos = 0;\n // At each step, we compute the next hash using two values:\n // - a value from the \"main queue\". If not all leaves have been consumed, we get the next leaf, otherwise we\n // get the next hash.\n // - depending on the flag, either another value for the \"main queue\" (merging branches) or an element from the\n // `proof` array.\n for (uint256 i = 0; i < totalHashes; i++) {\n bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];\n bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];\n hashes[i] = _hashPair(a, b);\n }\n\n if (totalHashes > 0) {\n return hashes[totalHashes - 1];\n } else if (leavesLen > 0) {\n return leaves[0];\n } else {\n return proof[0];\n }\n }\n\n function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {\n return a < b ? _efficientHash(a, b) : _efficientHash(b, a);\n }\n\n function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x00, a)\n mstore(0x20, b)\n value := keccak256(0x00, 0x40)\n }\n }\n}\n" }, "@openzeppelin/contracts/utils/introspection/IERC165.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" }, "@openzeppelin/contracts/utils/math/Math.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Down, // Toward negative infinity\n Up, // Toward infinity\n Zero // Toward zero\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a == 0 ? 0 : (a - 1) / b + 1;\n }\n\n /**\n * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n * with further edits by Uniswap Labs also under MIT license.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2^256 + prod0.\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(x, y, not(0))\n prod0 := mul(x, y)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division.\n if (prod1 == 0) {\n return prod0 / denominator;\n }\n\n // Make sure the result is less than 2^256. Also prevents denominator == 0.\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0].\n uint256 remainder;\n assembly {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n // See https://cs.stackexchange.com/q/138556/92363.\n\n // Does not overflow because the denominator cannot be zero at this stage in the function.\n uint256 twos = denominator & (~denominator + 1);\n assembly {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [prod1 prod0] by twos.\n prod0 := div(prod0, twos)\n\n // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from prod1 into prod0.\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv = 1 mod 2^4.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n // in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2^8\n inverse *= 2 - denominator * inverse; // inverse mod 2^16\n inverse *= 2 - denominator * inverse; // inverse mod 2^32\n inverse *= 2 - denominator * inverse; // inverse mod 2^64\n inverse *= 2 - denominator * inverse; // inverse mod 2^128\n inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inverse;\n return result;\n }\n }\n\n /**\n * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator,\n Rounding rounding\n ) internal pure returns (uint256) {\n uint256 result = mulDiv(x, y, denominator);\n if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n result += 1;\n }\n return result;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n *\n * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n }\n\n // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n //\n // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n //\n // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n //\n // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n uint256 result = 1 << (log2(a) >> 1);\n\n // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n // into the expected uint128 result.\n unchecked {\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n return min(result, a / result);\n }\n }\n\n /**\n * @notice Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 2, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 128;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 64;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 32;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 16;\n }\n if (value >> 8 > 0) {\n value >>= 8;\n result += 8;\n }\n if (value >> 4 > 0) {\n value >>= 4;\n result += 4;\n }\n if (value >> 2 > 0) {\n value >>= 2;\n result += 2;\n }\n if (value >> 1 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 10, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10**64) {\n value /= 10**64;\n result += 64;\n }\n if (value >= 10**32) {\n value /= 10**32;\n result += 32;\n }\n if (value >= 10**16) {\n value /= 10**16;\n result += 16;\n }\n if (value >= 10**8) {\n value /= 10**8;\n result += 8;\n }\n if (value >= 10**4) {\n value /= 10**4;\n result += 4;\n }\n if (value >= 10**2) {\n value /= 10**2;\n result += 2;\n }\n if (value >= 10**1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 256, rounded down, of a positive value.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 16;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 8;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 4;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 2;\n }\n if (value >> 8 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n }\n }\n}\n" }, "@openzeppelin/contracts/utils/math/SafeCast.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n *\n * _Available since v4.7._\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n require(value <= type(uint248).max, \"SafeCast: value doesn't fit in 248 bits\");\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n *\n * _Available since v4.7._\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n require(value <= type(uint240).max, \"SafeCast: value doesn't fit in 240 bits\");\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n *\n * _Available since v4.7._\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n require(value <= type(uint232).max, \"SafeCast: value doesn't fit in 232 bits\");\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n *\n * _Available since v4.2._\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n *\n * _Available since v4.7._\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n require(value <= type(uint216).max, \"SafeCast: value doesn't fit in 216 bits\");\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n *\n * _Available since v4.7._\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n require(value <= type(uint208).max, \"SafeCast: value doesn't fit in 208 bits\");\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n *\n * _Available since v4.7._\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n require(value <= type(uint200).max, \"SafeCast: value doesn't fit in 200 bits\");\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n *\n * _Available since v4.7._\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n require(value <= type(uint192).max, \"SafeCast: value doesn't fit in 192 bits\");\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n *\n * _Available since v4.7._\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n require(value <= type(uint184).max, \"SafeCast: value doesn't fit in 184 bits\");\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n *\n * _Available since v4.7._\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n require(value <= type(uint176).max, \"SafeCast: value doesn't fit in 176 bits\");\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n *\n * _Available since v4.7._\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n require(value <= type(uint168).max, \"SafeCast: value doesn't fit in 168 bits\");\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n *\n * _Available since v4.7._\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n require(value <= type(uint160).max, \"SafeCast: value doesn't fit in 160 bits\");\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n *\n * _Available since v4.7._\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n require(value <= type(uint152).max, \"SafeCast: value doesn't fit in 152 bits\");\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n *\n * _Available since v4.7._\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n require(value <= type(uint144).max, \"SafeCast: value doesn't fit in 144 bits\");\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n *\n * _Available since v4.7._\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n require(value <= type(uint136).max, \"SafeCast: value doesn't fit in 136 bits\");\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v2.5._\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n *\n * _Available since v4.7._\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n require(value <= type(uint120).max, \"SafeCast: value doesn't fit in 120 bits\");\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n *\n * _Available since v4.7._\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n require(value <= type(uint112).max, \"SafeCast: value doesn't fit in 112 bits\");\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n *\n * _Available since v4.7._\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n require(value <= type(uint104).max, \"SafeCast: value doesn't fit in 104 bits\");\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n *\n * _Available since v4.2._\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n *\n * _Available since v4.7._\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n require(value <= type(uint88).max, \"SafeCast: value doesn't fit in 88 bits\");\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n *\n * _Available since v4.7._\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n require(value <= type(uint80).max, \"SafeCast: value doesn't fit in 80 bits\");\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n *\n * _Available since v4.7._\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n require(value <= type(uint72).max, \"SafeCast: value doesn't fit in 72 bits\");\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v2.5._\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n *\n * _Available since v4.7._\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n require(value <= type(uint56).max, \"SafeCast: value doesn't fit in 56 bits\");\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n *\n * _Available since v4.7._\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n require(value <= type(uint48).max, \"SafeCast: value doesn't fit in 48 bits\");\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n *\n * _Available since v4.7._\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n require(value <= type(uint40).max, \"SafeCast: value doesn't fit in 40 bits\");\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v2.5._\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n *\n * _Available since v4.7._\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n require(value <= type(uint24).max, \"SafeCast: value doesn't fit in 24 bits\");\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v2.5._\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n *\n * _Available since v2.5._\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n *\n * _Available since v3.0._\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n *\n * _Available since v4.7._\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 248 bits\");\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n *\n * _Available since v4.7._\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 240 bits\");\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n *\n * _Available since v4.7._\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 232 bits\");\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n *\n * _Available since v4.7._\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 224 bits\");\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n *\n * _Available since v4.7._\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 216 bits\");\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n *\n * _Available since v4.7._\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 208 bits\");\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n *\n * _Available since v4.7._\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 200 bits\");\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n *\n * _Available since v4.7._\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 192 bits\");\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n *\n * _Available since v4.7._\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 184 bits\");\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n *\n * _Available since v4.7._\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 176 bits\");\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n *\n * _Available since v4.7._\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 168 bits\");\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n *\n * _Available since v4.7._\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 160 bits\");\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n *\n * _Available since v4.7._\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 152 bits\");\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n *\n * _Available since v4.7._\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 144 bits\");\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n *\n * _Available since v4.7._\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 136 bits\");\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 128 bits\");\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n *\n * _Available since v4.7._\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 120 bits\");\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n *\n * _Available since v4.7._\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 112 bits\");\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n *\n * _Available since v4.7._\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 104 bits\");\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n *\n * _Available since v4.7._\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 96 bits\");\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n *\n * _Available since v4.7._\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 88 bits\");\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n *\n * _Available since v4.7._\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 80 bits\");\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n *\n * _Available since v4.7._\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 72 bits\");\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 64 bits\");\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n *\n * _Available since v4.7._\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 56 bits\");\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n *\n * _Available since v4.7._\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 48 bits\");\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n *\n * _Available since v4.7._\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 40 bits\");\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 32 bits\");\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n *\n * _Available since v4.7._\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 24 bits\");\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 16 bits\");\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 8 bits\");\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n *\n * _Available since v3.0._\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" }, "@openzeppelin/contracts/utils/StorageSlot.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```\n * contract ERC1967 {\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\n */\nlibrary StorageSlot {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n}\n" }, "@openzeppelin/contracts/utils/Strings.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n" }, "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastValue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastValue;\n // Update the index for the moved value\n set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n bytes32[] memory store = _values(set._inner);\n bytes32[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" }, "contracts/config/bufferRateHandler/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { IFluidReserveContract } from \"../../reserve/interfaces/iReserveContract.sol\";\nimport { Error } from \"../error.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { FluidConfigHandler } from \"../fluidConfigHandler.sol\";\n\nimport { BigMathMinified } from \"../../libraries/bigMathMinified.sol\";\nimport { Structs as AdminModuleStructs } from \"../../liquidity/adminModule/structs.sol\";\n\nabstract contract Constants {\n IFluidReserveContract public immutable RESERVE_CONTRACT;\n IFluidLiquidity public immutable LIQUIDITY;\n\n /// @notice supply token at Liquidity which borrow rate is based on\n address public immutable SUPPLY_TOKEN;\n /// @notice borrow token at Liquidity for which the borrow rate is managed\n address public immutable BORROW_TOKEN;\n\n /// @notice buffer at kink1 for the rate. borrow rate = supply rate + buffer. In percent (100 = 1%, 1 = 0.01%)\n int256 public immutable RATE_BUFFER_KINK1;\n /// @notice buffer at kink2 for the rate. borrow rate = supply rate + buffer. In percent (100 = 1%, 1 = 0.01%)\n /// @dev only used if CURRENT borrow rate mode at Liquidity is V2 (with 2 kinks).\n int256 public immutable RATE_BUFFER_KINK2;\n\n /// @dev minimum percent difference to trigger an update. In percent (100 = 1%, 1 = 0.01%)\n uint256 public immutable MIN_UPDATE_DIFF;\n\n bytes32 internal immutable _LIQUDITY_SUPPLY_TOTAL_AMOUNTS_SLOT;\n bytes32 internal immutable _LIQUDITY_SUPPLY_EXCHANGE_PRICES_AND_CONFIG_SLOT;\n\n bytes32 internal immutable _LIQUDITY_BORROW_RATE_DATA_SLOT;\n\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xff;\n\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n uint256 internal constant FOUR_DECIMALS = 10000;\n}\n\nabstract contract Events {\n /// @notice emitted when borrow rate for `BORROW_TOKEN` is updated based on\n /// supply rate of `SUPPLY_TOKEN` + buffer.\n event LogUpdateRate(\n uint256 supplyRate,\n uint256 oldRateKink1,\n uint256 newRateKink1,\n uint256 oldRateKink2,\n uint256 newRateKink2\n );\n}\n\n/// @notice Sets borrow rate for `BORROW_TOKEN` at Liquidaty based on supply rate of `SUPPLY_TOKEN` + buffer.\ncontract FluidBufferRateHandler is Constants, Error, Events, FluidConfigHandler {\n /// @dev Validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidConfigError(ErrorTypes.BufferRateConfigHandler__AddressZero);\n }\n _;\n }\n\n /// @dev Validates that an address is a rebalancer (taken from reserve contract)\n modifier onlyRebalancer() {\n if (!RESERVE_CONTRACT.isRebalancer(msg.sender)) {\n revert FluidConfigError(ErrorTypes.BufferRateConfigHandler__Unauthorized);\n }\n _;\n }\n\n constructor(\n IFluidReserveContract reserveContract_,\n IFluidLiquidity liquidity_,\n address supplyToken_,\n address borrowToken_,\n int256 rateBufferKink1_,\n int256 rateBufferKink2_,\n uint256 minUpdateDiff_\n )\n validAddress(address(reserveContract_))\n validAddress(address(liquidity_))\n validAddress(supplyToken_)\n validAddress(borrowToken_)\n {\n if (\n minUpdateDiff_ == 0 ||\n // rate buffer should be within +100% to - 100%\n rateBufferKink1_ > 1e4 ||\n rateBufferKink1_ < -int256(1e4) ||\n rateBufferKink2_ > 1e4 ||\n rateBufferKink2_ < -int256(1e4)\n ) {\n revert FluidConfigError(ErrorTypes.BufferRateConfigHandler__InvalidParams);\n }\n\n RESERVE_CONTRACT = reserveContract_;\n LIQUIDITY = liquidity_;\n SUPPLY_TOKEN = supplyToken_;\n BORROW_TOKEN = borrowToken_;\n MIN_UPDATE_DIFF = minUpdateDiff_;\n\n RATE_BUFFER_KINK1 = rateBufferKink1_;\n RATE_BUFFER_KINK2 = rateBufferKink2_;\n\n _LIQUDITY_SUPPLY_TOTAL_AMOUNTS_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n supplyToken_\n );\n _LIQUDITY_SUPPLY_EXCHANGE_PRICES_AND_CONFIG_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n supplyToken_\n );\n\n _LIQUDITY_BORROW_RATE_DATA_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_RATE_DATA_MAPPING_SLOT,\n borrowToken_\n );\n }\n\n /// @inheritdoc FluidConfigHandler\n function configPercentDiff() public view override returns (uint256 configPercentDiff_) {\n uint256 rateConfig_ = LIQUIDITY.readFromStorage(_LIQUDITY_BORROW_RATE_DATA_SLOT);\n\n (uint256 newRateKink1_, uint256 newRateKink2_) = _calcBorrowRates(supplyTokenLendingRate(), rateConfig_);\n\n uint256 rateVersion_ = rateConfig_ & 0xF;\n if (rateVersion_ == 1) {\n uint256 oldRateKink1_ = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) &\n X16;\n configPercentDiff_ = _percentDiffForValue(oldRateKink1_, newRateKink1_);\n } else if (rateVersion_ == 2) {\n uint256 oldRateKink1_ = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) &\n X16;\n uint256 oldRateKink2_ = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) &\n X16;\n\n configPercentDiff_ = _percentDiffForValue(oldRateKink1_, newRateKink1_);\n uint256 rateKink2Diff_ = _percentDiffForValue(oldRateKink2_, newRateKink2_);\n // final diff = biggest diff between all config values\n configPercentDiff_ = configPercentDiff_ > rateKink2Diff_ ? configPercentDiff_ : rateKink2Diff_;\n } else {\n revert FluidConfigError(ErrorTypes.BufferRateConfigHandler__RateVersionUnsupported);\n }\n }\n\n /// @inheritdoc FluidConfigHandler\n function rebalance() external override onlyRebalancer {\n uint256 supplyLendingRate_ = supplyTokenLendingRate();\n uint256 rateConfig_ = LIQUIDITY.readFromStorage(_LIQUDITY_BORROW_RATE_DATA_SLOT);\n\n uint256 rateVersion_ = rateConfig_ & 0xF;\n if (rateVersion_ == 1) {\n _rebalanceRateV1(supplyLendingRate_, rateConfig_);\n } else if (rateVersion_ == 2) {\n _rebalanceRateV2(supplyLendingRate_, rateConfig_);\n } else {\n revert FluidConfigError(ErrorTypes.BufferRateConfigHandler__RateVersionUnsupported);\n }\n }\n\n /// @notice returns the current calculcated borrow rates at kink1 and kink 2 (for rate data v2).\n function calcBorrowRates() public view returns (uint256 rateKink1_, uint256 rateKink2_) {\n return _calcBorrowRates(supplyTokenLendingRate(), LIQUIDITY.readFromStorage(_LIQUDITY_BORROW_RATE_DATA_SLOT));\n }\n\n /// @notice get current `SUPPLY_TOKEN` lending `rate_` at Liquidity\n function supplyTokenLendingRate() public view returns (uint256 rate_) {\n // @dev logic here based on Liquidity Resolver .getOverallTokenData()\n uint256 totalAmounts_ = LIQUIDITY.readFromStorage(_LIQUDITY_SUPPLY_TOTAL_AMOUNTS_SLOT);\n\n // Extract supply & borrow amounts\n uint256 supplyRawInterest_ = totalAmounts_ & X64;\n supplyRawInterest_ =\n (supplyRawInterest_ >> DEFAULT_EXPONENT_SIZE) <<\n (supplyRawInterest_ & DEFAULT_EXPONENT_MASK);\n\n uint256 borrowRawInterest_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) &\n X64;\n borrowRawInterest_ =\n (borrowRawInterest_ >> DEFAULT_EXPONENT_SIZE) <<\n (borrowRawInterest_ & DEFAULT_EXPONENT_MASK);\n\n if (supplyRawInterest_ > 0) {\n uint256 exchangePriceAndConfig_ = LIQUIDITY.readFromStorage(\n _LIQUDITY_SUPPLY_EXCHANGE_PRICES_AND_CONFIG_SLOT\n );\n\n // use old exchange prices for supply rate to be at same level as borrow rate from storage.\n // Note the rate here can be a tiny bit with higher precision because we use borrowWithInterest_ / supplyWithInterest_\n // which has higher precision than the utilization used from storage in LiquidityCalcs\n uint256 supplyWithInterest_ = (supplyRawInterest_ *\n ((exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64)) /\n EXCHANGE_PRICES_PRECISION; // normalized from raw\n uint256 borrowWithInterest_ = (borrowRawInterest_ *\n ((exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64)) /\n EXCHANGE_PRICES_PRECISION; // normalized from raw\n\n uint256 borrowRate_ = exchangePriceAndConfig_ & X16;\n uint256 fee_ = (exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14;\n\n rate_ =\n (borrowRate_ * (FOUR_DECIMALS - fee_) * borrowWithInterest_) /\n (supplyWithInterest_ * FOUR_DECIMALS);\n }\n }\n\n /// @dev calculates current borrow rates at kinks for supply rate and current rate data\n function _calcBorrowRates(\n uint256 supplyRate_,\n uint256 rateConfig_\n ) internal view returns (uint256 rateKink1_, uint256 rateKink2_) {\n // rate can never be <0, > X16.\n rateKink1_ = (int256(supplyRate_) + RATE_BUFFER_KINK1) > 0\n ? uint256((int256(supplyRate_) + RATE_BUFFER_KINK1))\n : 0;\n // rate can never be > X16\n rateKink1_ = rateKink1_ > X16 ? X16 : rateKink1_;\n if ((rateConfig_ & 0xF) == 1) {\n // v1: only 1 kink\n // rate at last kink must always be <= rate at 100% utilization\n uint256 rateAtUtilizationMax_ = (rateConfig_ >>\n LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) & X16;\n if (rateKink1_ > rateAtUtilizationMax_) {\n rateKink1_ = rateAtUtilizationMax_;\n }\n } else {\n // v2: 2 kinks\n // rate can never be <0, > X16.\n rateKink2_ = (int256(supplyRate_) + RATE_BUFFER_KINK2) > 0\n ? uint256(int256(supplyRate_) + RATE_BUFFER_KINK2)\n : 0;\n // rate can never be > X16\n rateKink2_ = rateKink2_ > X16 ? X16 : rateKink2_;\n // rate at kink must always be <= rate at 100% utilization\n uint256 rateAtUtilizationMax_ = (rateConfig_ >>\n LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) & X16;\n if (rateKink1_ > rateAtUtilizationMax_) {\n rateKink1_ = rateAtUtilizationMax_;\n }\n if (rateKink2_ > rateAtUtilizationMax_) {\n rateKink2_ = rateAtUtilizationMax_;\n }\n }\n }\n\n /// @dev gets the percentage difference between `oldValue_` and `newValue_` in relation to `oldValue_`\n function _percentDiffForValue(\n uint256 oldValue_,\n uint256 newValue_\n ) internal pure returns (uint256 configPercentDiff_) {\n if (oldValue_ == newValue_) {\n return 0;\n }\n\n if (oldValue_ > newValue_) {\n // % of how much new value would be smaller\n configPercentDiff_ = oldValue_ - newValue_;\n // e.g. 10 - 8 = 2. 2 * 10000 / 10 -> 2000 (20%)\n } else {\n // % of how much new value would be bigger\n configPercentDiff_ = newValue_ - oldValue_;\n // e.g. 10 - 8 = 2. 2 * 10000 / 8 -> 2500 (25%)\n }\n\n configPercentDiff_ = (configPercentDiff_ * 1e4) / oldValue_;\n }\n\n /// @dev rebalances for a RateV1 config\n function _rebalanceRateV1(uint256 supplyRate_, uint256 rateConfig_) internal {\n AdminModuleStructs.RateDataV1Params memory rateData_;\n\n uint256 oldRateKink1_ = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;\n (rateData_.rateAtUtilizationKink, ) = _calcBorrowRates(supplyRate_, rateConfig_);\n\n // check if diff is enough to trigger update\n if (_percentDiffForValue(oldRateKink1_, rateData_.rateAtUtilizationKink) < MIN_UPDATE_DIFF) {\n revert FluidConfigError(ErrorTypes.BufferRateConfigHandler__NoUpdate);\n }\n\n rateData_.token = BORROW_TOKEN;\n // values that stay the same: kink, rate at 0%, rate at 100%\n rateData_.rateAtUtilizationZero =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) &\n X16;\n rateData_.kink = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16;\n rateData_.rateAtUtilizationMax =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) &\n X16;\n\n // trigger update\n AdminModuleStructs.RateDataV1Params[] memory params_ = new AdminModuleStructs.RateDataV1Params[](1);\n params_[0] = rateData_;\n LIQUIDITY.updateRateDataV1s(params_);\n\n // emit event\n emit LogUpdateRate(supplyRate_, oldRateKink1_, rateData_.rateAtUtilizationKink, 0, 0);\n }\n\n /// @dev rebalances for a RateV2 config\n function _rebalanceRateV2(uint256 supplyRate_, uint256 rateConfig_) internal {\n AdminModuleStructs.RateDataV2Params memory rateData_;\n\n uint256 oldRateKink1_ = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;\n uint256 oldRateKink2_ = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;\n (rateData_.rateAtUtilizationKink1, rateData_.rateAtUtilizationKink2) = _calcBorrowRates(\n supplyRate_,\n rateConfig_\n );\n\n // check if diff is enough to trigger update\n if (\n _percentDiffForValue(oldRateKink1_, rateData_.rateAtUtilizationKink1) < MIN_UPDATE_DIFF &&\n _percentDiffForValue(oldRateKink2_, rateData_.rateAtUtilizationKink2) < MIN_UPDATE_DIFF\n ) {\n revert FluidConfigError(ErrorTypes.BufferRateConfigHandler__NoUpdate);\n }\n\n rateData_.token = BORROW_TOKEN;\n // values that stay the same: kink1, kink2, rate at 0%, rate at 100%\n rateData_.rateAtUtilizationZero =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) &\n X16;\n rateData_.kink1 = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) & X16;\n rateData_.kink2 = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) & X16;\n rateData_.rateAtUtilizationMax =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) &\n X16;\n\n // trigger update\n AdminModuleStructs.RateDataV2Params[] memory params_ = new AdminModuleStructs.RateDataV2Params[](1);\n params_[0] = rateData_;\n LIQUIDITY.updateRateDataV2s(params_);\n\n // emit event\n emit LogUpdateRate(\n supplyRate_,\n oldRateKink1_,\n rateData_.rateAtUtilizationKink1,\n oldRateKink2_,\n rateData_.rateAtUtilizationKink2\n );\n }\n}\n" }, "contracts/config/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Error {\n error FluidConfigError(uint256 errorId_);\n}\n" }, "contracts/config/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | ExpandPercentConfigHandler | \n |__________________________________*/\n\n /// @notice thrown when an input address is zero\n uint256 internal constant ExpandPercentConfigHandler__AddressZero = 100001;\n\n /// @notice thrown when an unauthorized `msg.sender` calls a protected method\n uint256 internal constant ExpandPercentConfigHandler__Unauthorized = 100002;\n\n /// @notice thrown when invalid params are passed into a method\n uint256 internal constant ExpandPercentConfigHandler__InvalidParams = 100003;\n\n /// @notice thrown when no update is currently needed\n uint256 internal constant ExpandPercentConfigHandler__NoUpdate = 100004;\n\n /// @notice thrown when slot is not used, e.g. when borrow token is 0 there is no borrow data\n uint256 internal constant ExpandPercentConfigHandler__SlotDoesNotExist = 100005;\n\n /***********************************|\n | EthenaRateConfigHandler | \n |__________________________________*/\n\n /// @notice thrown when an input address is zero\n uint256 internal constant EthenaRateConfigHandler__AddressZero = 100011;\n\n /// @notice thrown when an unauthorized `msg.sender` calls a protected method\n uint256 internal constant EthenaRateConfigHandler__Unauthorized = 100012;\n\n /// @notice thrown when invalid params are passed into a method\n uint256 internal constant EthenaRateConfigHandler__InvalidParams = 100013;\n\n /// @notice thrown when no update is currently needed\n uint256 internal constant EthenaRateConfigHandler__NoUpdate = 100014;\n\n /***********************************|\n | MaxBorrowConfigHandler | \n |__________________________________*/\n\n /// @notice thrown when an input address is zero\n uint256 internal constant MaxBorrowConfigHandler__AddressZero = 100021;\n\n /// @notice thrown when an unauthorized `msg.sender` calls a protected method\n uint256 internal constant MaxBorrowConfigHandler__Unauthorized = 100022;\n\n /// @notice thrown when invalid params are passed into a method\n uint256 internal constant MaxBorrowConfigHandler__InvalidParams = 100023;\n\n /// @notice thrown when no update is currently needed\n uint256 internal constant MaxBorrowConfigHandler__NoUpdate = 100024;\n\n /***********************************|\n | BufferRateConfigHandler | \n |__________________________________*/\n\n /// @notice thrown when an input address is zero\n uint256 internal constant BufferRateConfigHandler__AddressZero = 100031;\n\n /// @notice thrown when an unauthorized `msg.sender` calls a protected method\n uint256 internal constant BufferRateConfigHandler__Unauthorized = 100032;\n\n /// @notice thrown when invalid params are passed into a method\n uint256 internal constant BufferRateConfigHandler__InvalidParams = 100033;\n\n /// @notice thrown when no update is currently needed\n uint256 internal constant BufferRateConfigHandler__NoUpdate = 100034;\n\n /// @notice thrown when rate data version is not supported\n uint256 internal constant BufferRateConfigHandler__RateVersionUnsupported = 100035;\n}\n" }, "contracts/config/ethenaRateHandler/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Events {\n /// @notice emitted when borrow magnifier is updated at vault\n event LogUpdateBorrowRateMagnifier(uint256 oldMagnifier, uint256 newMagnifier);\n}\n" }, "contracts/config/ethenaRateHandler/interfaces/iStakedUSDe.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\n\ninterface IStakedUSDe is IERC4626 {\n /// @notice The amount of the last asset distribution from the controller contract into this\n /// contract + any unvested remainder at that time\n function vestingAmount() external view returns (uint256);\n\n /// @notice The timestamp of the last asset distribution from the controller contract into this contract\n function lastDistributionTimestamp() external view returns (uint256);\n\n /// @notice Returns the amount of USDe tokens that are vested in the contract.\n function totalAssets() external view returns (uint256);\n}\n" }, "contracts/config/ethenaRateHandler/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\nimport { IFluidReserveContract } from \"../../reserve/interfaces/iReserveContract.sol\";\nimport { IFluidVaultT1 } from \"../../protocols/vault/interfaces/iVaultT1.sol\";\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { FluidVaultT1Admin } from \"../../protocols/vault/vaultT1/adminModule/main.sol\";\nimport { IStakedUSDe } from \"./interfaces/iStakedUSDe.sol\";\nimport { Variables } from \"./variables.sol\";\nimport { Events } from \"./events.sol\";\nimport { Error } from \"../error.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @notice Sets borrow rate for sUSDe/debtToken vaults based on sUSDe yield rate, by adjusting the borrowRateMagnifier\ncontract FluidEthenaRateConfigHandler is Variables, Error, Events {\n /// @dev Validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidConfigError(ErrorTypes.EthenaRateConfigHandler__AddressZero);\n }\n _;\n }\n\n /// @dev Validates that an address is a rebalancer (taken from reserve contract)\n modifier onlyRebalancer() {\n if (!RESERVE_CONTRACT.isRebalancer(msg.sender)) {\n revert FluidConfigError(ErrorTypes.EthenaRateConfigHandler__Unauthorized);\n }\n _;\n }\n\n constructor(\n IFluidReserveContract reserveContract_,\n IFluidLiquidity liquidity_,\n IFluidVaultT1 vault_,\n IStakedUSDe stakedUSDe_,\n address borrowToken_,\n uint256 ratePercentMargin_,\n uint256 maxRewardsDelay_,\n uint256 utilizationPenaltyStart_,\n uint256 utilization100PenaltyPercent_\n )\n validAddress(address(reserveContract_))\n validAddress(address(liquidity_))\n validAddress(address(vault_))\n validAddress(address(stakedUSDe_))\n validAddress(borrowToken_)\n {\n if (\n ratePercentMargin_ == 0 ||\n ratePercentMargin_ >= 1e4 ||\n maxRewardsDelay_ == 0 ||\n utilizationPenaltyStart_ >= 1e4 ||\n utilization100PenaltyPercent_ == 0\n ) {\n revert FluidConfigError(ErrorTypes.EthenaRateConfigHandler__InvalidParams);\n }\n\n RESERVE_CONTRACT = reserveContract_;\n LIQUIDITY = liquidity_;\n SUSDE = stakedUSDe_;\n VAULT = vault_;\n BORROW_TOKEN = borrowToken_;\n\n _LIQUDITY_BORROW_TOKEN_EXCHANGE_PRICES_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n borrowToken_\n );\n\n RATE_PERCENT_MARGIN = ratePercentMargin_;\n MAX_REWARDS_DELAY = maxRewardsDelay_;\n\n UTILIZATION_PENALTY_START = utilizationPenaltyStart_;\n UTILIZATION100_PENALTY_PERCENT = utilization100PenaltyPercent_;\n }\n\n /// @notice Rebalances the borrow rate magnifier for `VAULT` based on borrow rate at Liquidity in relation to\n /// sUSDe yield rate (`getSUSDEYieldRate()`).\n /// Emits `LogUpdateBorrowRateMagnifier` in case of update. Reverts if no update is needed.\n /// Can only be called by an authorized rebalancer.\n function rebalance() external onlyRebalancer {\n uint256 targetMagnifier_ = calculateMagnifier();\n uint256 currentMagnifier_ = currentMagnifier();\n\n // execute update on vault if necessary\n if (targetMagnifier_ == currentMagnifier_) {\n revert FluidConfigError(ErrorTypes.EthenaRateConfigHandler__NoUpdate);\n }\n\n FluidVaultT1Admin(address(VAULT)).updateBorrowRateMagnifier(targetMagnifier_);\n\n emit LogUpdateBorrowRateMagnifier(currentMagnifier_, targetMagnifier_);\n }\n\n /// @notice Calculates the new borrow rate magnifier based on sUSDe yield rate and utilization\n /// @return magnifier_ the calculated magnifier value.\n function calculateMagnifier() public view returns (uint256 magnifier_) {\n uint256 sUSDeYieldRate_ = getSUSDeYieldRate();\n uint256 exchangePriceAndConfig_ = LIQUIDITY.readFromStorage(_LIQUDITY_BORROW_TOKEN_EXCHANGE_PRICES_SLOT);\n\n uint256 utilization_ = (exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14;\n\n // calculate target borrow rate. scaled by 1e18.\n // borrow rate is based on sUSDeYieldRate_ and a margin that goes to lenders\n // e.g. when RATE_PERCENT_MARGIN = 1000 (10%), then borrow rate will be 90% of the sUSDe yield rate\n // e.g. when sUSDe yield is 60%, borrow rate would be 54%\n uint256 targetBorrowRate_ = (sUSDeYieldRate_ * (1e4 - RATE_PERCENT_MARGIN)) / 1e4;\n\n if (utilization_ > UTILIZATION_PENALTY_START) {\n // above UTILIZATION_PENALTY_START (e.g. 90%), penalty should rise linearly according to UTILIZATION100_PENALTY_PERCENT\n // e.g. from 10% margin at 90% utilization to -3% penalty at 100% utilization\n // so from +RATE_PERCENT_MARGIN at UTILIZATION_PENALTY_START to -UTILIZATION100_PENALTY_PERCENT at 100%\n if (utilization_ < 1e4) {\n uint256 utilizationAbovePenaltyStart_ = utilization_ - UTILIZATION_PENALTY_START;\n uint256 penaltyUtilizationDiff_ = 1e4 - UTILIZATION_PENALTY_START;\n uint256 penaltyRateDiff_ = RATE_PERCENT_MARGIN + UTILIZATION100_PENALTY_PERCENT;\n\n // e.g. when current utilization = 96%, start penalty utilization = 90%, penalty at 100 = 3%, rate margin = 90%:\n // utilizationAbovePenaltyStart_ = 600 (6%)\n // penaltyUtilizationDiff_ = 1000 (10%)\n // penaltyRateDiff_ = 1000 + 300 = 1300 (13%)\n // marginAfterPenalty_ = 1300 * 600 / 1000 = 780 (7.8%)\n uint256 marginAfterPenalty_ = (penaltyRateDiff_ * utilizationAbovePenaltyStart_) /\n penaltyUtilizationDiff_;\n\n // e.g. when sUSDe yield is 60%, borrow rate would become 58.68% (from 60% * (90% + 7.8%) / 100% )\n targetBorrowRate_ = (sUSDeYieldRate_ * ((1e4 - RATE_PERCENT_MARGIN) + marginAfterPenalty_)) / 1e4;\n } else {\n // above 100% utilization, cap at -UTILIZATION100_PENALTY_PERCENT penalty\n targetBorrowRate_ = (sUSDeYieldRate_ * (1e4 + UTILIZATION100_PENALTY_PERCENT)) / 1e4;\n }\n }\n\n // get current neutral borrow rate at Liquidity (without any magnifier).\n // exchangePriceAndConfig slot at Liquidity, first 16 bits\n uint256 liquidityBorrowRate_ = exchangePriceAndConfig_ & X16;\n\n if (liquidityBorrowRate_ == 0) {\n return 1e4;\n }\n\n // calculate magnifier needed to reach target borrow rate.\n // liquidityBorrowRate_ * x = targetBorrowRate_. so x = targetBorrowRate_ / liquidityBorrowRate_.\n // must scale liquidityBorrowRate_ from 1e2 to 1e18 as targetBorrowRate_ is in 1e18. magnifier itself is scaled\n // by 1e4 (1x = 10000)\n magnifier_ = (1e4 * targetBorrowRate_) / (liquidityBorrowRate_ * 1e16);\n\n // make sure magnifier is within allowed limits\n if (magnifier_ < _MIN_MAGNIFIER) {\n return _MIN_MAGNIFIER;\n }\n if (magnifier_ > _MAX_MAGNIFIER) {\n return _MAX_MAGNIFIER;\n }\n }\n\n /// @notice returns the currently configured borrow magnifier at the `VAULT`.\n function currentMagnifier() public view returns (uint256) {\n // read borrow rate magnifier from Vault `vaultVariables2` located in storage slot 1, 16 bits from 16-31\n return (VAULT.readFromStorage(bytes32(uint256(1))) >> 16) & X16;\n }\n\n /// @notice calculates updated vesting yield rate based on `vestingAmount` and `totalAssets` of StakedUSDe contract\n /// @return rate_ sUSDe yearly yield rate scaled by 1e18 (1e18 = 1%, 1e20 = 100%)\n function getSUSDeYieldRate() public view returns (uint256 rate_) {\n if (block.timestamp > SUSDE.lastDistributionTimestamp() + _SUSDE_VESTING_PERIOD + MAX_REWARDS_DELAY) {\n // if rewards update on StakedUSDe contract is delayed by more than `MAX_REWARDS_DELAY`, we use rate as 0\n // as we can't know if e.g. funding would have gone negative and there are indeed no rewards.\n return 0;\n }\n\n // vestingAmount is yield per 8 hours (`SUSDE_VESTING_PERIOD`)\n rate_ = (SUSDE.vestingAmount() * 1e20) / SUSDE.totalAssets(); // 8 hours rate\n // turn into yearly yield\n rate_ = (rate_ * 365 * 24 hours) / _SUSDE_VESTING_PERIOD; // 365 days * 24 hours / 8 hours -> rate_ * 1095\n }\n}\n" }, "contracts/config/ethenaRateHandler/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\nimport { IFluidReserveContract } from \"../../reserve/interfaces/iReserveContract.sol\";\nimport { IFluidVaultT1 } from \"../../protocols/vault/interfaces/iVaultT1.sol\";\nimport { IStakedUSDe } from \"./interfaces/iStakedUSDe.sol\";\n\nabstract contract Constants {\n IFluidReserveContract public immutable RESERVE_CONTRACT;\n IFluidLiquidity public immutable LIQUIDITY;\n IFluidVaultT1 public immutable VAULT;\n IStakedUSDe public immutable SUSDE;\n address public immutable BORROW_TOKEN;\n\n /// @notice sUSDe vesting yield reward rate percent margin that goes to lenders\n /// e.g. RATE_PERCENT_MARGIN = 10% then borrow rate for debt token ends up as 90% of the sUSDe yield.\n /// (in 1e2: 100% = 10_000; 1% = 100)\n uint256 public immutable RATE_PERCENT_MARGIN;\n\n /// @notice max delay in seconds for rewards update after vesting period ended, after which we assume rate is 0.\n /// e.g. 15 min\n uint256 public immutable MAX_REWARDS_DELAY;\n\n /// @notice utilization penalty start point (in 1e2: 100% = 10_000; 1% = 100). above this, a penalty percent\n /// is applied, to incentivize deleveraging.\n uint256 public immutable UTILIZATION_PENALTY_START;\n /// @notice penalty percent target at 100%, on top of sUSDe yield rate if utilization is above UTILIZATION_PENALTY_START\n /// (in 1e2: 100% = 10_000; 1% = 100)\n uint256 public immutable UTILIZATION100_PENALTY_PERCENT;\n\n bytes32 internal immutable _LIQUDITY_BORROW_TOKEN_EXCHANGE_PRICES_SLOT;\n\n /// @dev vesting period defined as private constant on StakedUSDe contract\n uint256 internal constant _SUSDE_VESTING_PERIOD = 8 hours;\n\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant _MIN_MAGNIFIER = 1e4; // min magnifier is always at least 1x (10000)\n uint256 internal constant _MAX_MAGNIFIER = 65535; // max magnifier to fit in storage slot is 65535 (16 bits)\n}\n\nabstract contract Variables is Constants {}\n" }, "contracts/config/expandPercentHandler/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { BigMathMinified } from \"../../libraries/bigMathMinified.sol\";\nimport { IFluidReserveContract } from \"../../reserve/interfaces/iReserveContract.sol\";\nimport { Structs as AdminModuleStructs } from \"../../liquidity/adminModule/structs.sol\";\nimport { Error } from \"../error.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\nabstract contract Constants {\n IFluidReserveContract public immutable RESERVE_CONTRACT;\n IFluidLiquidity public immutable LIQUIDITY;\n address public immutable PROTOCOL;\n address public immutable WITHDRAW_TOKEN;\n address public immutable BORROW_TOKEN;\n\n uint256 public immutable BORROW_CHECKPOINT1;\n uint256 public immutable BORROW_CHECKPOINT2;\n uint256 public immutable BORROW_CHECKPOINT3;\n uint256 public immutable BORROW_EXPAND_UNTIL_CHECKPOINT1;\n uint256 public immutable BORROW_EXPAND_UNTIL_CHECKPOINT2;\n uint256 public immutable BORROW_EXPAND_UNTIL_CHECKPOINT3;\n uint256 public immutable BORROW_EXPAND_ABOVE_CHECKPOINT3;\n\n uint256 public immutable WITHDRAW_CHECKPOINT1;\n uint256 public immutable WITHDRAW_CHECKPOINT2;\n uint256 public immutable WITHDRAW_CHECKPOINT3;\n uint256 public immutable WITHDRAW_EXPAND_UNTIL_CHECKPOINT1;\n uint256 public immutable WITHDRAW_EXPAND_UNTIL_CHECKPOINT2;\n uint256 public immutable WITHDRAW_EXPAND_UNTIL_CHECKPOINT3;\n uint256 public immutable WITHDRAW_EXPAND_ABOVE_CHECKPOINT3;\n\n bytes32 internal immutable _LIQUDITY_WITHDRAW_TOKEN_EXCHANGE_PRICES_SLOT;\n bytes32 internal immutable _LIQUDITY_BORROW_TOKEN_EXCHANGE_PRICES_SLOT;\n\n bytes32 internal immutable _LIQUDITY_PROTOCOL_SUPPLY_SLOT;\n bytes32 internal immutable _LIQUDITY_PROTOCOL_BORROW_SLOT;\n\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xff;\n\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n}\n\nabstract contract Events {\n /// @notice emitted when withdraw limit expand percent is updated\n event LogUpdateWithdrawLimitExpansion(uint256 supply, uint256 oldExpandPercent, uint256 newExpandPercent);\n\n /// @notice emitted when borrow limit expand percent is updated\n event LogUpdateBorrowLimitExpansion(uint256 borrow, uint256 oldExpandPercent, uint256 newExpandPercent);\n}\n\nabstract contract Structs {\n struct LimitCheckPoints {\n uint256 tvlCheckPoint1; // e.g. 20M\n uint256 expandPercentUntilCheckPoint1; // e.g. 25%\n uint256 tvlCheckPoint2; // e.g. 30M\n uint256 expandPercentUntilCheckPoint2; // e.g. 20%\n uint256 tvlCheckPoint3; // e.g. 40M\n uint256 expandPercentUntilCheckPoint3; // e.g. 15%\n uint256 expandPercentAboveCheckPoint3; // e.g. 10%\n }\n}\n\n/// @notice Sets limits on Liquidity for a protocol based on TVL checkpoints.\ncontract FluidExpandPercentConfigHandler is Constants, Error, Events, Structs {\n /// @dev Validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidConfigError(ErrorTypes.ExpandPercentConfigHandler__AddressZero);\n }\n _;\n }\n\n /// @dev Validates that an address is a rebalancer (taken from reserve contract)\n modifier onlyRebalancer() {\n if (!RESERVE_CONTRACT.isRebalancer(msg.sender)) {\n revert FluidConfigError(ErrorTypes.ExpandPercentConfigHandler__Unauthorized);\n }\n _;\n }\n\n constructor(\n IFluidReserveContract reserveContract_,\n IFluidLiquidity liquidity_,\n address protocol_,\n address withdrawToken_, // can be unused in some cases (e.g. StETH)\n address borrowToken_, // can be unused in some cases (e.g. Lending)\n LimitCheckPoints memory withdrawCheckPoints_, // can be skipped if withdrawToken is not set.\n LimitCheckPoints memory borrowCheckPoints_ // can be skipped if borrowToken_ is not set.\n ) validAddress(address(reserveContract_)) validAddress(address(liquidity_)) validAddress(protocol_) {\n RESERVE_CONTRACT = reserveContract_;\n LIQUIDITY = liquidity_;\n PROTOCOL = protocol_;\n WITHDRAW_TOKEN = withdrawToken_;\n BORROW_TOKEN = borrowToken_;\n\n // set withdraw limit values\n if (withdrawToken_ == address(0)) {\n if (borrowToken_ == address(0)) {\n revert FluidConfigError(ErrorTypes.ExpandPercentConfigHandler__InvalidParams);\n }\n\n _LIQUDITY_PROTOCOL_SUPPLY_SLOT = bytes32(0);\n } else {\n _LIQUDITY_PROTOCOL_SUPPLY_SLOT = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT,\n protocol_,\n withdrawToken_\n );\n _LIQUDITY_WITHDRAW_TOKEN_EXCHANGE_PRICES_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n withdrawToken_\n );\n\n _validateLimitCheckPoints(withdrawCheckPoints_);\n\n WITHDRAW_CHECKPOINT1 = withdrawCheckPoints_.tvlCheckPoint1;\n WITHDRAW_CHECKPOINT2 = withdrawCheckPoints_.tvlCheckPoint2;\n WITHDRAW_CHECKPOINT3 = withdrawCheckPoints_.tvlCheckPoint3;\n WITHDRAW_EXPAND_UNTIL_CHECKPOINT1 = withdrawCheckPoints_.expandPercentUntilCheckPoint1;\n WITHDRAW_EXPAND_UNTIL_CHECKPOINT2 = withdrawCheckPoints_.expandPercentUntilCheckPoint2;\n WITHDRAW_EXPAND_UNTIL_CHECKPOINT3 = withdrawCheckPoints_.expandPercentUntilCheckPoint3;\n WITHDRAW_EXPAND_ABOVE_CHECKPOINT3 = withdrawCheckPoints_.expandPercentAboveCheckPoint3;\n }\n\n // set borrow limit values\n if (borrowToken_ == address(0)) {\n _LIQUDITY_PROTOCOL_BORROW_SLOT = bytes32(0);\n } else {\n _validateLimitCheckPoints(borrowCheckPoints_);\n\n _LIQUDITY_PROTOCOL_BORROW_SLOT = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT,\n protocol_,\n borrowToken_\n );\n _LIQUDITY_BORROW_TOKEN_EXCHANGE_PRICES_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n borrowToken_\n );\n\n BORROW_CHECKPOINT1 = borrowCheckPoints_.tvlCheckPoint1;\n BORROW_CHECKPOINT2 = borrowCheckPoints_.tvlCheckPoint2;\n BORROW_CHECKPOINT3 = borrowCheckPoints_.tvlCheckPoint3;\n BORROW_EXPAND_UNTIL_CHECKPOINT1 = borrowCheckPoints_.expandPercentUntilCheckPoint1;\n BORROW_EXPAND_UNTIL_CHECKPOINT2 = borrowCheckPoints_.expandPercentUntilCheckPoint2;\n BORROW_EXPAND_UNTIL_CHECKPOINT3 = borrowCheckPoints_.expandPercentUntilCheckPoint3;\n BORROW_EXPAND_ABOVE_CHECKPOINT3 = borrowCheckPoints_.expandPercentAboveCheckPoint3;\n }\n }\n\n /// @notice returns `PROTOCOL` total supply at Liquidity\n function getProtocolSupplyData()\n public\n view\n returns (uint256 supply_, uint256 oldExpandPercent_, uint256 userSupplyData_)\n {\n if (_LIQUDITY_PROTOCOL_SUPPLY_SLOT == bytes32(0)) {\n revert FluidConfigError(ErrorTypes.ExpandPercentConfigHandler__SlotDoesNotExist);\n }\n userSupplyData_ = LIQUIDITY.readFromStorage(_LIQUDITY_PROTOCOL_SUPPLY_SLOT); // total storage slot\n\n oldExpandPercent_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;\n\n // get supply in raw converted from BigNumber\n supply_ = BigMathMinified.fromBigNumber(\n (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (userSupplyData_ & 1 == 1) {\n uint256 exchangePrice_ = ((LIQUIDITY.readFromStorage(_LIQUDITY_WITHDRAW_TOKEN_EXCHANGE_PRICES_SLOT) >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64);\n\n supply_ = (supply_ * exchangePrice_) / 1e12; // convert raw to normal amount\n }\n }\n\n /// @notice returns `PROTOCOL` total borrow at Liquidity\n function getProtocolBorrowData()\n public\n view\n returns (uint256 borrow_, uint256 oldExpandPercent_, uint256 userBorrowData_)\n {\n if (_LIQUDITY_PROTOCOL_BORROW_SLOT == bytes32(0)) {\n revert FluidConfigError(ErrorTypes.ExpandPercentConfigHandler__SlotDoesNotExist);\n }\n userBorrowData_ = LIQUIDITY.readFromStorage(_LIQUDITY_PROTOCOL_BORROW_SLOT); // total storage slot\n\n oldExpandPercent_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;\n\n // get borrow in raw converted from BigNumber\n borrow_ = BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (userBorrowData_ & 1 == 1) {\n uint256 exchangePrice_ = ((LIQUIDITY.readFromStorage(_LIQUDITY_BORROW_TOKEN_EXCHANGE_PRICES_SLOT) >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64);\n\n borrow_ = (borrow_ * exchangePrice_) / 1e12; // convert raw to normal amount\n }\n }\n\n /// @notice Rebalances the configs for `PROTOCOL` at Fluid Liquidity based on protocol total supply & total borrow.\n /// Emits `LogUpdateWithdrawLimitExpansion` or `LogUpdateBorrowLimitExpansion` if any update is executed.\n /// Reverts if no update is needed.\n /// Can only be called by an authorized rebalancer.\n function rebalance() external onlyRebalancer {\n bool anyUpdateDone_;\n if (WITHDRAW_TOKEN != address(0)) {\n // check update withdrawal limits based on protocol supply\n anyUpdateDone_ = _updateWithdrawLimits();\n }\n\n if (BORROW_TOKEN != address(0)) {\n // check update borrow limits based on protocol borrow\n anyUpdateDone_ = _updateBorrowLimits() || anyUpdateDone_;\n }\n\n if (!anyUpdateDone_) {\n revert FluidConfigError(ErrorTypes.ExpandPercentConfigHandler__NoUpdate);\n }\n }\n\n /***********************************|\n | INTERNALS | \n |__________________________________*/\n\n function _updateWithdrawLimits() internal returns (bool updated_) {\n (uint256 supply_, uint256 oldExpandPercent_, uint256 userSupplyData_) = getProtocolSupplyData();\n\n // get current expand percent for supply_\n uint256 newExpandPercent_;\n if (supply_ < WITHDRAW_CHECKPOINT1) {\n newExpandPercent_ = WITHDRAW_EXPAND_UNTIL_CHECKPOINT1;\n } else if (supply_ < WITHDRAW_CHECKPOINT2) {\n newExpandPercent_ = WITHDRAW_EXPAND_UNTIL_CHECKPOINT2;\n } else if (supply_ < WITHDRAW_CHECKPOINT3) {\n newExpandPercent_ = WITHDRAW_EXPAND_UNTIL_CHECKPOINT3;\n } else {\n newExpandPercent_ = WITHDRAW_EXPAND_ABOVE_CHECKPOINT3;\n }\n\n // check if not already set to that value\n if (oldExpandPercent_ == newExpandPercent_) {\n return false;\n }\n\n // execute update at Liquidity\n AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_ = new AdminModuleStructs.UserSupplyConfig[](1);\n userSupplyConfigs_[0] = AdminModuleStructs.UserSupplyConfig({\n user: PROTOCOL,\n token: WITHDRAW_TOKEN,\n mode: uint8(userSupplyData_ & 1), // first bit\n expandPercent: newExpandPercent_,\n expandDuration: (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24, // set same as old\n baseWithdrawalLimit: BigMathMinified.fromBigNumber(\n (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n ) // set same as old\n });\n LIQUIDITY.updateUserSupplyConfigs(userSupplyConfigs_);\n\n emit LogUpdateWithdrawLimitExpansion(supply_, oldExpandPercent_, newExpandPercent_);\n\n return true;\n }\n\n function _updateBorrowLimits() internal returns (bool updated_) {\n (uint256 borrow_, uint256 oldExpandPercent_, uint256 userBorrowData_) = getProtocolBorrowData();\n\n // get current expand percent for borrow_\n uint256 newExpandPercent_;\n if (borrow_ < BORROW_CHECKPOINT1) {\n newExpandPercent_ = BORROW_EXPAND_UNTIL_CHECKPOINT1;\n } else if (borrow_ < BORROW_CHECKPOINT2) {\n newExpandPercent_ = BORROW_EXPAND_UNTIL_CHECKPOINT2;\n } else if (borrow_ < BORROW_CHECKPOINT3) {\n newExpandPercent_ = BORROW_EXPAND_UNTIL_CHECKPOINT3;\n } else {\n newExpandPercent_ = BORROW_EXPAND_ABOVE_CHECKPOINT3;\n }\n\n // check if not already set to that value\n if (oldExpandPercent_ == newExpandPercent_) {\n return false;\n }\n\n // execute update at Liquidity\n AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_ = new AdminModuleStructs.UserBorrowConfig[](1);\n userBorrowConfigs_[0] = AdminModuleStructs.UserBorrowConfig({\n user: PROTOCOL,\n token: BORROW_TOKEN,\n mode: uint8(userBorrowData_ & 1), // first bit\n expandPercent: newExpandPercent_,\n expandDuration: (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24, // set same as old\n baseDebtCeiling: BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n ), // set same as old\n maxDebtCeiling: BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n ) // set same as old\n });\n LIQUIDITY.updateUserBorrowConfigs(userBorrowConfigs_);\n\n emit LogUpdateBorrowLimitExpansion(borrow_, oldExpandPercent_, newExpandPercent_);\n\n return true;\n }\n\n function _validateLimitCheckPoints(LimitCheckPoints memory checkPoints_) internal pure {\n if (\n checkPoints_.tvlCheckPoint1 == 0 ||\n checkPoints_.expandPercentUntilCheckPoint1 == 0 ||\n checkPoints_.tvlCheckPoint2 == 0 ||\n checkPoints_.expandPercentUntilCheckPoint2 == 0 ||\n checkPoints_.tvlCheckPoint3 == 0 ||\n checkPoints_.expandPercentUntilCheckPoint3 == 0 ||\n checkPoints_.expandPercentAboveCheckPoint3 == 0\n ) {\n revert FluidConfigError(ErrorTypes.ExpandPercentConfigHandler__InvalidParams);\n }\n }\n}\n" }, "contracts/config/fluidConfigHandler.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidConfigHandler } from \"./interfaces/iFluidConfigHandler.sol\";\n\n/// @title FluidConfigHandler\n/// @notice Base contract that any Fluid Config Handler must implement\nabstract contract FluidConfigHandler is IFluidConfigHandler {\n /// @inheritdoc IFluidConfigHandler\n function configPercentDiff() public view virtual returns (uint256 configPercentDiff_);\n\n /// @inheritdoc IFluidConfigHandler\n function rebalance() external virtual;\n}\n" }, "contracts/config/interfaces/iFluidConfigHandler.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidConfigHandler {\n /// @notice returns how much the new config would be different from current config in percent (100 = 1%, 1 = 0.01%).\n function configPercentDiff() external view returns (uint256 configPercentDiff_);\n\n /// @notice Rebalances the configs at Fluid Liquidity based on config handler target.\n /// Reverts if no update is needed.\n /// Can only be called by an authorized rebalancer.\n function rebalance() external;\n}\n" }, "contracts/config/maxBorrowHandler/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\nimport { IFluidLiquidityResolver } from \"../../periphery/resolvers/liquidity/iLiquidityResolver.sol\";\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { BigMathMinified } from \"../../libraries/bigMathMinified.sol\";\nimport { LiquidityCalcs } from \"../../libraries/liquidityCalcs.sol\";\nimport { IFluidReserveContract } from \"../../reserve/interfaces/iReserveContract.sol\";\nimport { Structs as AdminModuleStructs } from \"../../liquidity/adminModule/structs.sol\";\nimport { Error } from \"../error.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\nabstract contract Constants {\n IFluidReserveContract public immutable RESERVE_CONTRACT;\n IFluidLiquidity public immutable LIQUIDITY;\n IFluidLiquidityResolver public immutable LIQUIDITY_RESOLVER;\n address public immutable PROTOCOL;\n address public immutable BORROW_TOKEN;\n\n /// @dev max utilization of total supply that will be set as max borrow limit. In percent (100 = 1%, 1 = 0.01%)\n uint256 public immutable MAX_UTILIZATION;\n\n /// @dev minimum percent difference to trigger an update. In percent (100 = 1%, 1 = 0.01%)\n uint256 public immutable MIN_UPDATE_DIFF;\n\n bytes32 internal immutable _LIQUDITY_PROTOCOL_BORROW_SLOT;\n\n uint256 internal constant MAX_UTILIZATION_PRECISION = 1e4;\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xff;\n\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n}\n\nabstract contract Events {\n /// @notice emitted when borrow max limit is updated\n event LogUpdateBorrowMaxDebtCeiling(\n uint256 totalSupplyNormal,\n uint256 oldMaxDebtCeilingRaw,\n uint256 maxDebtCeilingRaw,\n uint256 borrowExchangePrice\n );\n}\n\n/// @notice Sets max borrow limit for a protocol on Liquidity based on utilization of total supply of the same borrow token\ncontract FluidMaxBorrowConfigHandler is Constants, Error, Events {\n /// @dev Validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidConfigError(ErrorTypes.MaxBorrowConfigHandler__AddressZero);\n }\n _;\n }\n\n /// @dev Validates that an address is a rebalancer (taken from reserve contract)\n modifier onlyRebalancer() {\n if (!RESERVE_CONTRACT.isRebalancer(msg.sender)) {\n revert FluidConfigError(ErrorTypes.MaxBorrowConfigHandler__Unauthorized);\n }\n _;\n }\n\n constructor(\n IFluidReserveContract reserveContract_,\n IFluidLiquidity liquidity_,\n IFluidLiquidityResolver liquidityResolver_,\n address protocol_,\n address borrowToken_,\n uint256 maxUtilization_,\n uint256 minUpdateDiff_\n )\n validAddress(address(reserveContract_))\n validAddress(address(liquidity_))\n validAddress(address(liquidityResolver_))\n validAddress(protocol_)\n validAddress(borrowToken_)\n {\n RESERVE_CONTRACT = reserveContract_;\n LIQUIDITY = liquidity_;\n LIQUIDITY_RESOLVER = liquidityResolver_;\n PROTOCOL = protocol_;\n BORROW_TOKEN = borrowToken_;\n\n if (maxUtilization_ > MAX_UTILIZATION_PRECISION || minUpdateDiff_ == 0) {\n revert FluidConfigError(ErrorTypes.MaxBorrowConfigHandler__InvalidParams);\n }\n\n _LIQUDITY_PROTOCOL_BORROW_SLOT = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT,\n protocol_,\n borrowToken_\n );\n\n MAX_UTILIZATION = maxUtilization_;\n MIN_UPDATE_DIFF = minUpdateDiff_;\n }\n\n /// @notice returns `BORROW_TOKEN` total supply at Liquidity (in normal).\n function getTotalSupply() public view returns (uint256 totalSupplyNormal_) {\n (totalSupplyNormal_, ) = _getTotalSupply();\n }\n\n /// @notice returns the currently set max debt ceiling (in raw for mode with interest!).\n function currentMaxDebtCeiling() public view returns (uint256 maxDebtCeiling_) {\n return\n BigMathMinified.fromBigNumber(\n (LIQUIDITY.readFromStorage(_LIQUDITY_PROTOCOL_BORROW_SLOT) >>\n LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n }\n\n /// @notice returns the max debt ceiling that should be set according to current state (in normal).\n function calcMaxDebtCeilingNormal() public view returns (uint256 maxDebtCeilingNormal_) {\n (uint256 maxDebtCeilingRaw_, , uint256 borrowExchangePrice_, , ) = _calcMaxDebtCeiling();\n // convert to normal\n maxDebtCeilingNormal_ = (maxDebtCeilingRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION;\n }\n\n /// @notice returns the max debt ceiling that should be set according to current state (in raw for mode with interest!).\n function calcMaxDebtCeiling() public view returns (uint256 maxDebtCeiling_) {\n (maxDebtCeiling_, , , , ) = _calcMaxDebtCeiling();\n }\n\n /// @notice returns how much new config would be different from current config in percent (100 = 1%, 1 = 0.01%).\n function configPercentDiff() public view returns (uint256 configPercentDiff_) {\n (uint256 maxDebtCeilingRaw_, , , uint256 userBorrowData_, ) = _calcMaxDebtCeiling();\n\n (configPercentDiff_, ) = _configPercentDiff(userBorrowData_, maxDebtCeilingRaw_);\n }\n\n /// @notice Rebalances the configs for `PROTOCOL` at Fluid Liquidity based on protocol total supply & total borrow.\n /// Emits `LogUpdateBorrowMaxDebtCeiling` if update is executed.\n /// Reverts if no update is needed.\n /// Can only be called by an authorized rebalancer.\n function rebalance() external onlyRebalancer {\n if (!_updateBorrowLimits()) {\n revert FluidConfigError(ErrorTypes.MaxBorrowConfigHandler__NoUpdate);\n }\n }\n\n /***********************************|\n | INTERNALS | \n |__________________________________*/\n\n function _getTotalSupply() public view returns (uint256 totalSupplyNormal_, uint256 borrowExchangePrice_) {\n uint256 supplyExchangePrice_;\n\n (supplyExchangePrice_, borrowExchangePrice_) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY_RESOLVER.getExchangePricesAndConfig(BORROW_TOKEN)\n );\n\n totalSupplyNormal_ = LiquidityCalcs.getTotalSupply(\n LIQUIDITY_RESOLVER.getTotalAmounts(BORROW_TOKEN),\n supplyExchangePrice_\n );\n }\n\n function _calcMaxDebtCeiling()\n internal\n view\n returns (\n uint256 maxDebtCeilingRaw_,\n uint256 totalSupplyNormal_,\n uint256 borrowExchangePrice_,\n uint256 userBorrowData_,\n uint256 baseDebtCeilingRaw_\n )\n {\n (totalSupplyNormal_, borrowExchangePrice_) = _getTotalSupply();\n\n uint256 maxDebtCeilingNormal_ = (MAX_UTILIZATION * totalSupplyNormal_) / MAX_UTILIZATION_PRECISION;\n\n // turn into maxDebtCeiling Raw\n maxDebtCeilingRaw_ = (maxDebtCeilingNormal_ * EXCHANGE_PRICES_PRECISION) / borrowExchangePrice_;\n\n userBorrowData_ = LIQUIDITY.readFromStorage(_LIQUDITY_PROTOCOL_BORROW_SLOT); // total storage slot\n\n baseDebtCeilingRaw_ = BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (baseDebtCeilingRaw_ > maxDebtCeilingRaw_) {\n // max debt ceiling can never be < base debt ceiling\n maxDebtCeilingRaw_ = baseDebtCeilingRaw_;\n }\n }\n\n function _configPercentDiff(\n uint256 userBorrowData_,\n uint256 maxDebtCeilingRaw_\n ) internal pure returns (uint256 configPercentDiff_, uint256 oldMaxDebtCeilingRaw_) {\n oldMaxDebtCeilingRaw_ = BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (oldMaxDebtCeilingRaw_ == maxDebtCeilingRaw_) {\n return (0, oldMaxDebtCeilingRaw_);\n }\n\n if (oldMaxDebtCeilingRaw_ > maxDebtCeilingRaw_) {\n // % of how much new max debt ceiling would be smaller\n configPercentDiff_ = oldMaxDebtCeilingRaw_ - maxDebtCeilingRaw_;\n // e.g. 10 - 8 = 2. 2 * 10000 / 10 -> 2000 (20%)\n } else {\n // % of how much new max debt ceiling would be bigger\n configPercentDiff_ = maxDebtCeilingRaw_ - oldMaxDebtCeilingRaw_;\n // e.g. 10 - 8 = 2. 2 * 10000 / 8 -> 2500 (25%)\n }\n\n configPercentDiff_ = (configPercentDiff_ * 1e4) / oldMaxDebtCeilingRaw_;\n }\n\n function _updateBorrowLimits() internal returns (bool updated_) {\n (\n uint256 maxDebtCeilingRaw_,\n uint256 totalSupplyNormal_,\n uint256 borrowExchangePrice_,\n uint256 userBorrowData_,\n uint256 baseDebtCeilingRaw_\n ) = _calcMaxDebtCeiling();\n\n (uint256 configPercentDiff_, uint256 oldMaxDebtCeilingRaw_) = _configPercentDiff(\n userBorrowData_,\n maxDebtCeilingRaw_\n );\n\n // check if min config deviation is reached\n if (configPercentDiff_ < MIN_UPDATE_DIFF) {\n return false;\n }\n\n // execute update at Liquidity\n AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_ = new AdminModuleStructs.UserBorrowConfig[](1);\n userBorrowConfigs_[0] = AdminModuleStructs.UserBorrowConfig({\n user: PROTOCOL,\n token: BORROW_TOKEN,\n mode: uint8(userBorrowData_ & 1), // first bit\n expandPercent: (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14, // set same as old\n expandDuration: (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24, // set same as old\n baseDebtCeiling: baseDebtCeilingRaw_, // set same as old\n maxDebtCeiling: maxDebtCeilingRaw_\n });\n LIQUIDITY.updateUserBorrowConfigs(userBorrowConfigs_);\n\n emit LogUpdateBorrowMaxDebtCeiling(\n totalSupplyNormal_,\n oldMaxDebtCeilingRaw_,\n maxDebtCeilingRaw_,\n borrowExchangePrice_\n );\n\n return true;\n }\n}\n" }, "contracts/infiniteProxy/error.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ncontract Error {\n error FluidInfiniteProxyError(uint256 errorId_);\n}\n" }, "contracts/infiniteProxy/errorTypes.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | Infinite proxy | \n |__________________________________*/\n\n /// @notice thrown when an implementation does not exist\n uint256 internal constant InfiniteProxy__ImplementationNotExist = 50001;\n}\n" }, "contracts/infiniteProxy/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Events {\n /// @notice emitted when a new admin is set\n event LogSetAdmin(address indexed oldAdmin, address indexed newAdmin);\n\n /// @notice emitted when a new dummy implementation is set\n event LogSetDummyImplementation(address indexed oldDummyImplementation, address indexed newDummyImplementation);\n\n /// @notice emitted when a new implementation is set with certain sigs\n event LogSetImplementation(address indexed implementation, bytes4[] sigs);\n\n /// @notice emitted when an implementation is removed\n event LogRemoveImplementation(address indexed implementation);\n}\n" }, "contracts/infiniteProxy/interfaces/iProxy.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IProxy {\n function setAdmin(address newAdmin_) external;\n\n function setDummyImplementation(address newDummyImplementation_) external;\n\n function addImplementation(address implementation_, bytes4[] calldata sigs_) external;\n\n function removeImplementation(address implementation_) external;\n\n function getAdmin() external view returns (address);\n\n function getDummyImplementation() external view returns (address);\n\n function getImplementationSigs(address impl_) external view returns (bytes4[] memory);\n\n function getSigsImplementation(bytes4 sig_) external view returns (address);\n\n function readFromStorage(bytes32 slot_) external view returns (uint256 result_);\n}\n" }, "contracts/infiniteProxy/proxy.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { Events } from \"./events.sol\";\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { Error } from \"./error.sol\";\nimport { StorageRead } from \"../libraries/storageRead.sol\";\n\ncontract CoreInternals is StorageRead, Events, Error {\n struct SigsSlot {\n bytes4[] value;\n }\n\n /// @dev Storage slot with the admin of the contract.\n /// This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n /// validated in the constructor.\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /// @dev Storage slot with the address of the current dummy-implementation.\n /// This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n /// validated in the constructor.\n bytes32 internal constant _DUMMY_IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /// @dev use EIP1967 proxy slot (see _DUMMY_IMPLEMENTATION_SLOT) except for first 4 bytes,\n // which are set to 0. This is combined with a sig which will be set in those first 4 bytes\n bytes32 internal constant _SIG_SLOT_BASE = 0x000000003ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /// @dev Returns the storage slot which stores the sigs array set for the implementation.\n function _getSlotImplSigsSlot(address implementation_) internal pure returns (bytes32) {\n return keccak256(abi.encode(\"eip1967.proxy.implementation\", implementation_));\n }\n\n /// @dev Returns the storage slot which stores the implementation address for the function sig.\n function _getSlotSigsImplSlot(bytes4 sig_) internal pure returns (bytes32 result_) {\n assembly {\n // or operator sets sig_ in first 4 bytes with rest of bytes32 having default value of _SIG_SLOT_BASE\n result_ := or(_SIG_SLOT_BASE, sig_)\n }\n }\n\n /// @dev Returns an address `data_` located at `slot_`.\n function _getAddressSlot(bytes32 slot_) internal view returns (address data_) {\n assembly {\n data_ := sload(slot_)\n }\n }\n\n /// @dev Sets an address `data_` located at `slot_`.\n function _setAddressSlot(bytes32 slot_, address data_) internal {\n assembly {\n sstore(slot_, data_)\n }\n }\n\n /// @dev Returns an `SigsSlot` with member `value` located at `slot`.\n function _getSigsSlot(bytes32 slot_) internal pure returns (SigsSlot storage _r) {\n assembly {\n _r.slot := slot_\n }\n }\n\n /// @dev Sets new implementation and adds mapping from implementation to sigs and sig to implementation.\n function _setImplementationSigs(address implementation_, bytes4[] memory sigs_) internal {\n require(sigs_.length != 0, \"no-sigs\");\n bytes32 slot_ = _getSlotImplSigsSlot(implementation_);\n bytes4[] memory sigsCheck_ = _getSigsSlot(slot_).value;\n require(sigsCheck_.length == 0, \"implementation-already-exist\");\n\n for (uint256 i; i < sigs_.length; i++) {\n bytes32 sigSlot_ = _getSlotSigsImplSlot(sigs_[i]);\n require(_getAddressSlot(sigSlot_) == address(0), \"sig-already-exist\");\n _setAddressSlot(sigSlot_, implementation_);\n }\n\n _getSigsSlot(slot_).value = sigs_;\n emit LogSetImplementation(implementation_, sigs_);\n }\n\n /// @dev Removes implementation and the mappings corresponding to it.\n function _removeImplementationSigs(address implementation_) internal {\n bytes32 slot_ = _getSlotImplSigsSlot(implementation_);\n bytes4[] memory sigs_ = _getSigsSlot(slot_).value;\n require(sigs_.length != 0, \"implementation-not-exist\");\n\n for (uint256 i; i < sigs_.length; i++) {\n bytes32 sigSlot_ = _getSlotSigsImplSlot(sigs_[i]);\n _setAddressSlot(sigSlot_, address(0));\n }\n\n delete _getSigsSlot(slot_).value;\n emit LogRemoveImplementation(implementation_);\n }\n\n /// @dev Returns bytes4[] sigs from implementation address. If implemenatation is not registered then returns empty array.\n function _getImplementationSigs(address implementation_) internal view returns (bytes4[] memory) {\n bytes32 slot_ = _getSlotImplSigsSlot(implementation_);\n return _getSigsSlot(slot_).value;\n }\n\n /// @dev Returns implementation address from bytes4 sig. If sig is not registered then returns address(0).\n function _getSigImplementation(bytes4 sig_) internal view returns (address implementation_) {\n bytes32 slot_ = _getSlotSigsImplSlot(sig_);\n return _getAddressSlot(slot_);\n }\n\n /// @dev Returns the current admin.\n function _getAdmin() internal view returns (address) {\n return _getAddressSlot(_ADMIN_SLOT);\n }\n\n /// @dev Returns the current dummy-implementation.\n function _getDummyImplementation() internal view returns (address) {\n return _getAddressSlot(_DUMMY_IMPLEMENTATION_SLOT);\n }\n\n /// @dev Stores a new address in the EIP1967 admin slot.\n function _setAdmin(address newAdmin_) internal {\n address oldAdmin_ = _getAdmin();\n require(newAdmin_ != address(0), \"ERC1967: new admin is the zero address\");\n _setAddressSlot(_ADMIN_SLOT, newAdmin_);\n emit LogSetAdmin(oldAdmin_, newAdmin_);\n }\n\n /// @dev Stores a new address in the EIP1967 implementation slot.\n function _setDummyImplementation(address newDummyImplementation_) internal {\n address oldDummyImplementation_ = _getDummyImplementation();\n _setAddressSlot(_DUMMY_IMPLEMENTATION_SLOT, newDummyImplementation_);\n emit LogSetDummyImplementation(oldDummyImplementation_, newDummyImplementation_);\n }\n}\n\ncontract AdminInternals is CoreInternals {\n /// @dev Only admin guard\n modifier onlyAdmin() {\n require(msg.sender == _getAdmin(), \"only-admin\");\n _;\n }\n\n constructor(address admin_, address dummyImplementation_) {\n _setAdmin(admin_);\n _setDummyImplementation(dummyImplementation_);\n }\n\n /// @dev Sets new admin.\n function setAdmin(address newAdmin_) external onlyAdmin {\n _setAdmin(newAdmin_);\n }\n\n /// @dev Sets new dummy-implementation.\n function setDummyImplementation(address newDummyImplementation_) external onlyAdmin {\n _setDummyImplementation(newDummyImplementation_);\n }\n\n /// @dev Adds new implementation address.\n function addImplementation(address implementation_, bytes4[] calldata sigs_) external onlyAdmin {\n _setImplementationSigs(implementation_, sigs_);\n }\n\n /// @dev Removes an existing implementation address.\n function removeImplementation(address implementation_) external onlyAdmin {\n _removeImplementationSigs(implementation_);\n }\n}\n\n/// @title Proxy\n/// @notice This abstract contract provides a fallback function that delegates all calls to another contract using the EVM.\n/// It implements the Instadapp infinite-proxy: https://github.com/Instadapp/infinite-proxy\nabstract contract Proxy is AdminInternals {\n constructor(address admin_, address dummyImplementation_) AdminInternals(admin_, dummyImplementation_) {}\n\n /// @dev Returns admin's address.\n function getAdmin() external view returns (address) {\n return _getAdmin();\n }\n\n /// @dev Returns dummy-implementations's address.\n function getDummyImplementation() external view returns (address) {\n return _getDummyImplementation();\n }\n\n /// @dev Returns bytes4[] sigs from implementation address If not registered then returns empty array.\n function getImplementationSigs(address impl_) external view returns (bytes4[] memory) {\n return _getImplementationSigs(impl_);\n }\n\n /// @dev Returns implementation address from bytes4 sig. If sig is not registered then returns address(0).\n function getSigsImplementation(bytes4 sig_) external view returns (address) {\n return _getSigImplementation(sig_);\n }\n\n /// @dev Fallback function that delegates calls to the address returned by Implementations registry.\n fallback() external payable {\n address implementation_;\n assembly {\n // get slot for sig and directly SLOAD implementation address from storage at that slot\n implementation_ := sload(\n // same as in `_getSlotSigsImplSlot()` but we must also load msg.sig from calldata.\n // msg.sig is first 4 bytes of calldata, so we can use calldataload(0) with a mask\n or(\n // or operator sets sig_ in first 4 bytes with rest of bytes32 having default value of _SIG_SLOT_BASE\n _SIG_SLOT_BASE,\n and(calldataload(0), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)\n )\n )\n }\n\n if (implementation_ == address(0)) {\n revert FluidInfiniteProxyError(ErrorTypes.InfiniteProxy__ImplementationNotExist);\n }\n\n // Delegate the current call to `implementation`.\n // This does not return to its internall call site, it will return directly to the external caller.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation_, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n if eq(result, 0) {\n // delegatecall returns 0 on error.\n revert(0, returndatasize())\n }\n\n return(0, returndatasize())\n }\n }\n\n receive() external payable {\n // receive method can never have calldata in EVM so no need for any logic here\n }\n}\n" }, "contracts/libraries/bigMathMinified.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.\n/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision\n/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can\n/// result in significant gas cost reduction due to storage space reduction.\n/// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision.\n/// @dev roundUp is more like a increase 1, which happens everytime for the same number.\n/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number.\nlibrary BigMathMinified {\n /// @dev constants to use for `roundUp` input param to increase readability\n bool internal constant ROUND_DOWN = false;\n bool internal constant ROUND_UP = true;\n\n /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).\n /// e.g.:\n /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]\n /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)\n /// => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000\n /// ^-------------------- 51(exponent) -------------- ^\n /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563)\n /// exponent = 0011,0011 (51)\n /// bigNumber = 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 (572493200179)\n ///\n /// @param normal number which needs to be converted into Big Number\n /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))\n /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))\n /// @param roundUp signals if result should be rounded down or up\n /// @return bigNumber converted bigNumber (coefficient << exponent)\n function toBigNumber(\n uint256 normal,\n uint256 coefficientSize,\n uint256 exponentSize,\n bool roundUp\n ) internal pure returns (uint256 bigNumber) {\n assembly {\n let lastBit_\n let number_ := normal\n if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit_ := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit_ := add(lastBit_, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit_ := add(lastBit_, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit_ := add(lastBit_, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit_ := add(lastBit_, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit_ := add(lastBit_, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit_ := add(lastBit_, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit_ := add(lastBit_, 1)\n }\n if gt(number_, 0) {\n lastBit_ := add(lastBit_, 1)\n }\n if lt(lastBit_, coefficientSize) {\n // for throw exception\n lastBit_ := coefficientSize\n }\n let exponent := sub(lastBit_, coefficientSize)\n let coefficient := shr(exponent, normal)\n if and(roundUp, gt(exponent, 0)) {\n // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number\n coefficient := add(coefficient, 1)\n if eq(shl(coefficientSize, 1), coefficient) {\n // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits\n // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.\n coefficient := shl(sub(coefficientSize, 1), 1)\n exponent := add(exponent, 1)\n }\n }\n if iszero(lt(exponent, shl(exponentSize, 1))) {\n // if exponent is >= exponentSize, the normal number is too big to fit within\n // BigNumber with too small sizes for coefficient and exponent\n revert(0, 0)\n }\n bigNumber := shl(exponentSize, coefficient)\n bigNumber := add(bigNumber, exponent)\n }\n }\n\n /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`\n function fromBigNumber(\n uint256 bigNumber,\n uint256 exponentSize,\n uint256 exponentMask\n ) internal pure returns (uint256 normal) {\n assembly {\n let coefficient := shr(exponentSize, bigNumber)\n let exponent := and(bigNumber, exponentMask)\n normal := shl(exponent, coefficient)\n }\n }\n\n /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).\n /// e.g.\n /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111\n /// lastBit = ^--------------------------------- 83 ----------------------------------------^\n function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {\n assembly {\n let number_ := normal\n if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit := add(lastBit, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit := add(lastBit, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit := add(lastBit, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit := add(lastBit, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit := add(lastBit, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit := add(lastBit, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit := add(lastBit, 1)\n }\n if gt(number_, 0) {\n lastBit := add(lastBit, 1)\n }\n }\n }\n}\n" }, "contracts/libraries/bigMathUnsafe.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n// todo: decide for license\n\n// Note: It's advised to use BigMathMinified which covers +90% of use cases, other functions need more thorough audits and testing\n\n/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.\n/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision\n/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can\n/// result in significant gas cost reduction due to storage space reduction.\n/// Also note, Valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision\n/// @dev roundUp is more like a increase 1, which happens everytime for the same number.\n/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once to the same number.\nlibrary BigMathUnsafe {\n /// @dev constants to use for `roundUp` input param to increase readability\n bool internal constant ROUND_DOWN = false;\n bool internal constant ROUND_UP = true;\n\n /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).\n /// e.g.:\n /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]\n /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)\n /// => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000\n /// ^-------------------- 51(exponent) -------------- ^\n /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563)\n /// exponent = 0011,0011 (51)\n /// bigNumber = 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 (572493200179)\n ///\n /// @param normal number which needs to be converted into Big Number\n /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))\n /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))\n /// @param roundUp signals if result should be rounded down or up\n /// @return bigNumber converted bigNumber (coefficient << exponent)\n function toBigNumber(\n uint256 normal,\n uint256 coefficientSize,\n uint256 exponentSize,\n bool roundUp\n ) internal pure returns (uint256 bigNumber) {\n assembly {\n let lastBit_\n let number_ := normal\n if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit_ := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit_ := add(lastBit_, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit_ := add(lastBit_, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit_ := add(lastBit_, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit_ := add(lastBit_, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit_ := add(lastBit_, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit_ := add(lastBit_, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit_ := add(lastBit_, 1)\n }\n if gt(number_, 0) {\n lastBit_ := add(lastBit_, 1)\n }\n if lt(lastBit_, coefficientSize) {\n // for throw exception\n lastBit_ := coefficientSize\n }\n let exponent := sub(lastBit_, coefficientSize)\n let coefficient := shr(exponent, normal)\n if and(roundUp, gt(exponent, 0)) {\n // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number\n coefficient := add(coefficient, 1)\n if eq(shl(coefficientSize, 1), coefficient) {\n // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits\n // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.\n coefficient := shl(sub(coefficientSize, 1), 1)\n exponent := add(exponent, 1)\n }\n }\n if iszero(lt(exponent, shl(exponentSize, 1))) {\n // if exponent is >= exponentSize, the normal number is too big to fit within\n // BigNumber with too small sizes for coefficient and exponent\n revert(0, 0)\n }\n bigNumber := shl(exponentSize, coefficient)\n bigNumber := add(bigNumber, exponent)\n }\n }\n\n /// @dev see {BigMathUnsafe-toBigNumber}, but returns coefficient and exponent too\n function toBigNumberExtended(\n uint256 normal,\n uint256 coefficientSize,\n uint256 exponentSize,\n bool roundUp\n ) internal pure returns (uint256 coefficient, uint256 exponent, uint256 bigNumber) {\n assembly {\n let lastBit_\n let number_ := normal\n if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit_ := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit_ := add(lastBit_, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit_ := add(lastBit_, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit_ := add(lastBit_, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit_ := add(lastBit_, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit_ := add(lastBit_, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit_ := add(lastBit_, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit_ := add(lastBit_, 1)\n }\n if gt(number_, 0) {\n lastBit_ := add(lastBit_, 1)\n }\n if lt(lastBit_, coefficientSize) {\n // for throw exception\n lastBit_ := coefficientSize\n }\n exponent := sub(lastBit_, coefficientSize)\n coefficient := shr(exponent, normal)\n if and(roundUp, gt(exponent, 0)) {\n // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number\n coefficient := add(coefficient, 1)\n if eq(shl(coefficientSize, 1), coefficient) {\n // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits\n // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.\n coefficient := shl(sub(coefficientSize, 1), 1)\n exponent := add(exponent, 1)\n }\n }\n if iszero(lt(exponent, shl(exponentSize, 1))) {\n // if exponent is >= exponentSize, the normal number is too big to fit within\n // BigNumber with too small sizes for coefficient and exponent\n revert(0, 0)\n }\n bigNumber := shl(exponentSize, coefficient)\n bigNumber := add(bigNumber, exponent)\n }\n }\n\n /// @dev get `normal` number from BigNumber `coefficient` and `exponent`.\n /// e.g.:\n /// (coefficient[32bits], exponent[8bits])[40bits] => (normal)\n /// (2236301563, 51) = 100001010100101101000000111110110000000000000000000000000000000000000000000000000\n /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563)\n /// exponent = 0011,0011 (51)\n /// normal = 10000101010010110100000011111011000000000000000000000000000000000000000000000000000 (5035703442907428892442624)\n /// ^-------------------- 51(exponent) -------------- ^\n function fromBigNumber(uint256 coefficient, uint256 exponent) internal pure returns (uint256 normal) {\n assembly {\n normal := shl(exponent, coefficient)\n }\n }\n\n /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`\n function fromBigNumber(\n uint256 bigNumber,\n uint256 exponentSize,\n uint256 exponentMask\n ) internal pure returns (uint256 normal) {\n assembly {\n let coefficient := shr(exponentSize, bigNumber)\n let exponent := and(bigNumber, exponentMask)\n normal := shl(exponent, coefficient)\n }\n }\n\n /// @dev multiplies a `normal` number with a `bigNumber1` and then divides by `bigNumber2`, with `exponentSize` and\n /// `exponentMask` being used for both bigNumbers.\n /// e.g.\n /// res = normal * bigNumber1 / bigNumber2\n /// normal: normal number 281474976710656\n /// bigNumber1: bigNumber 265046402172 [(0011,1101,1011,0101,1111,1111,0010,0100)Coefficient, (0111,1100)Exponent]\n /// bigNumber2: bigNumber 178478830197 [(0010 1001 1000 1110 0010 1010 1101 0010)Coefficient, (0111 0101)Exponent\n /// @return res normal number 53503841411969141\n function mulDivNormal(\n uint256 normal,\n uint256 bigNumber1,\n uint256 bigNumber2,\n uint256 exponentSize,\n uint256 exponentMask\n ) internal pure returns (uint256 res) {\n assembly {\n let coefficient1_ := shr(exponentSize, bigNumber1)\n let exponent1_ := and(bigNumber1, exponentMask)\n let coefficient2_ := shr(exponentSize, bigNumber2)\n let exponent2_ := and(bigNumber2, exponentMask)\n let X := gt(exponent1_, exponent2_) // bigNumber2 > bigNumber1\n if X {\n coefficient1_ := shl(sub(exponent1_, exponent2_), coefficient1_)\n }\n if iszero(X) {\n coefficient2_ := shl(sub(exponent2_, exponent1_), coefficient2_)\n }\n // todo should we do this not in assembly so normal SafeMath checks work? e.g. divide by 0 etc.\n res := div(mul(normal, coefficient1_), coefficient2_)\n }\n }\n\n /// @dev decompiles a `bigNumber` into `coefficient` and `exponent`, based on `exponentSize` and `exponentMask`.\n /// e.g.\n /// bigNumber[40bits] => coefficient[32bits], exponent[8bits]\n /// 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 =>\n /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563)\n /// exponent = 0011,0011 (51)\n function decompileBigNumber(\n uint256 bigNumber,\n uint256 exponentSize,\n uint256 exponentMask\n ) internal pure returns (uint256 coefficient, uint256 exponent) {\n assembly {\n coefficient := shr(exponentSize, bigNumber)\n exponent := and(bigNumber, exponentMask)\n }\n }\n\n /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).\n /// e.g.\n /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111\n /// lastBit = ^--------------------------------- 83 ----------------------------------------^\n function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {\n assembly {\n let number_ := normal\n if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit := add(lastBit, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit := add(lastBit, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit := add(lastBit, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit := add(lastBit, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit := add(lastBit, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit := add(lastBit, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit := add(lastBit, 1)\n }\n if gt(number_, 0) {\n lastBit := add(lastBit, 1)\n }\n }\n }\n\n /// @dev multiplies a `bigNumber` with normal `number1` and then divides by normal `number2`. `exponentSize` and `exponentMask`\n /// are used for the input `bigNumber` and the `result` is a BigNumber with `coefficientSize` and `exponentSize`.\n /// @param bigNumber Coefficient | Exponent. Eg: 8 bits coefficient (1101 0101) and 4 bits exponent (0011)\n /// @param number1 normal number. Eg:- 32421421413532\n /// @param number2 normal number. Eg:- 91897739843913\n /// @param precisionBits precision bits should be set such that, (((Coefficient * number1) << precisionBits) / number2) > max coefficient possible\n /// @param coefficientSize coefficient size. Eg: 8 bits, 56 btits, etc\n /// @param exponentSize exponent size. Eg: 4 bits, 12 btits, etc\n /// @param exponentMask exponent mask. (1 << exponentSize) - 1\n /// @param roundUp is true then roundUp, default it's round down\n /// @return result bigNumber * number1 / number2. Note bigNumber can't get directly multiplied or divide by normal numbers.\n /// TODO: Add an example which can help in better understanding.\n /// Didn't converted into assembly as overflow checks are good to have\n function mulDivBigNumber(\n uint256 bigNumber,\n uint256 number1,\n uint256 number2,\n uint256 precisionBits,\n uint256 coefficientSize,\n uint256 exponentSize,\n uint256 exponentMask,\n bool roundUp\n ) internal pure returns (uint256 result) {\n uint256 _resultNumerator = (((bigNumber >> exponentSize) * number1) << precisionBits) / number2;\n uint256 diff = mostSignificantBit(_resultNumerator) - coefficientSize;\n _resultNumerator = _resultNumerator >> diff;\n _resultNumerator = roundUp ? _resultNumerator + 1 : _resultNumerator;\n uint256 _exponent = (bigNumber & exponentMask) + diff - precisionBits;\n\n if (_exponent <= exponentMask) {\n result = (_resultNumerator << exponentSize) + _exponent;\n } else {\n revert(\"exponent-overflow\");\n }\n }\n\n // TODO: this function probably has some bugs & needs some updates to make it more efficient\n /// @dev multiplies a `bigNumber1` with another `bigNumber2`.\n /// e.g. res = bigNumber1 * bigNumber2 = [(coe1, exp1) * (coe2, exp2)] >> decimal\n /// = (coe1*coe2>>overflow, exp1+exp2+overflow-decimal)\n /// @param bigNumber1 BigNumber format with coefficient and exponent\n /// @param bigNumber2 BigNumber format with coefficient and exponent\n /// @param coefficientSize max size of coefficient, same for both `bigNumber1` and `bigNumber2`\n /// @param exponentSize max size of exponent, same for both `bigNumber1` and `bigNumber2`\n /// @param decimal decimals in bits\n /// @return res BigNumber format with coefficient and exponent\n function mulBigNumber(\n uint256 bigNumber1,\n uint256 bigNumber2,\n uint256 coefficientSize,\n uint256 exponentSize,\n uint256 decimal\n ) internal pure returns (uint256 res) {\n uint256 coefficient1_;\n uint256 exponent1_;\n uint256 coefficient2_;\n uint256 exponent2_;\n\n assembly {\n if eq(bigNumber1, 0) {\n stop()\n }\n\n if eq(bigNumber2, 0) {\n stop()\n }\n\n let exponentMask_ := sub(shl(exponentSize, 1), 1)\n coefficient1_ := shr(exponentSize, bigNumber1)\n exponent1_ := and(bigNumber1, exponentMask_)\n coefficient2_ := shr(exponentSize, bigNumber2)\n exponent2_ := and(bigNumber2, exponentMask_)\n }\n\n // when exponent is 0, it means coefficient last bit could be less than _coefficientSize and we need to calculate the length using mostSignificantBit()\n // when exponent is greater than 0, coefficient length will always be the same as _coefficientSize\n uint256 coefficientLen1_ = exponent1_ == 0 ? mostSignificantBit(coefficient1_) : coefficientSize;\n uint256 coefficientLen2_ = exponent2_ == 0 ? mostSignificantBit(coefficient2_) : coefficientSize;\n\n assembly {\n let overflowLen_\n let resCoefficient_ := mul(coefficient1_, coefficient2_)\n let midLen_ := add(coefficientLen1_, coefficientLen2_)\n // the (coefficientLen1_ * coefficientLen2_) length will be among\n // (coefficientLen1_'s length + coefficientLen2_'s length) and (coefficientLen1_'s length + coefficientLen2_'s length -1)\n if eq(and(resCoefficient_, shl(sub(midLen_, 1), 1)), 0) {\n midLen_ := sub(midLen_, 1)\n }\n if gt(midLen_, coefficientSize) {\n overflowLen_ := sub(midLen_, coefficientSize)\n resCoefficient_ := shr(overflowLen_, resCoefficient_)\n }\n let resExponent_ := add(add(exponent1_, exponent2_), overflowLen_)\n let cond_ := gt(add(resExponent_, coefficientSize), decimal)\n if iszero(cond_) {\n stop()\n }\n cond_ := gt(decimal, resExponent_)\n if cond_ {\n resCoefficient_ := shr(sub(decimal, resExponent_), resCoefficient_)\n resExponent_ := 0\n }\n if iszero(cond_) {\n resExponent_ := sub(resExponent_, decimal)\n if gt(resExponent_, sub(shl(exponentSize, 1), 1)) {\n revert(0, 0) // overflow error\n }\n }\n res := add(shl(exponentSize, resCoefficient_), resExponent_)\n }\n }\n\n /// @dev divides a `bigNumber1` by `bigNumber2`.\n /// e.g. res = bigNumber1 / bigNumber2 = [(coe1, exp1) / (coe2, exp2)] << decimal\n /// = ((coe1< 0 BigMath numbers have max precision)\n /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.\n /// - bigNumber1 (debt factor) always have exponent >= 1 & <= 16384\n /// - bigNumber2 (connection factor) always have exponent >= 1 & <= 32767 (15 bits)\n /// - bigNumber2 always >= bigNumber1 (connection factor can never be < base branch debt factor)\n /// - as a result of previous points, numbers must never be 0\n /// - normal is positionRawDebt and is always within 10000 and type(int128).max\n /// @return normal * bigNumber1 / bigNumber2\n function mulDivNormal(uint256 normal, uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {\n unchecked {\n // exponent2_ - exponent1_\n uint netExponent_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) - (bigNumber1 & EXPONENT_MAX_DEBT_FACTOR);\n if (netExponent_ < 129) {\n // (normal * coefficient1_) / (coefficient2_ << netExponent_);\n return ((normal * (bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR)) /\n ((bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR) << netExponent_));\n }\n // else:\n // biggest possible nominator: type(int128).max * 35bits max = 5846006549323611672814739330865132078589370433536\n // smallest possible denominator: 17179869184 << 129 (= 1 << 163) = 11692013098647223345629478661730264157247460343808\n // -> can only ever be 0\n return 0;\n }\n }\n\n /// @dev multiplies a `bigNumber` with normal `number1` and then divides by `TWO_POWER_64`.\n /// @dev For vault's use case (calculating new branch debt factor after liquidation):\n /// - number1 is debtFactor, intialized as TWO_POWER_64 and reduced from there, hence it's always <= TWO_POWER_64 and always > 0.\n /// - bigNumber is branch debt factor, which starts as ((X35 << 15) | (1 << 14)) and reduces from there.\n /// - bigNumber must have have exponent size 15 bits and be >= 1 & <= 16384\n /// - bigNumber must have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)\n /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.\n /// @param bigNumber Coefficient | Exponent.\n /// @param number1 normal number.\n /// @return result bigNumber * number1 / TWO_POWER_64.\n function mulDivBigNumber(uint256 bigNumber, uint256 number1) internal pure returns (uint256 result) {\n // using unchecked as we are only at 1 place in Vault and it won't overflow there.\n unchecked {\n uint256 _resultNumerator = (bigNumber >> EXPONENT_SIZE_DEBT_FACTOR) * number1; // bigNumber coefficient * normal number\n // 99% chances are that most sig bit should be 64 + 35 - 1 or 64 + 35 - 2\n // diff = mostSigBit. Can only ever be >= 35 and <= 98\n uint256 diff = (_resultNumerator > TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1)\n ? COEFFICIENT_PLUS_PRECISION\n : (_resultNumerator > TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1_MINUS_1)\n ? COEFFICIENT_PLUS_PRECISION_MINUS_1\n : BigMathMinified.mostSignificantBit(_resultNumerator);\n\n // diff = difference in bits to make the _resultNumerator 35 bits again\n diff = diff - COEFFICIENT_SIZE_DEBT_FACTOR;\n _resultNumerator = _resultNumerator >> diff;\n // starting exponent is 16384, so exponent should never get 0 here\n result = (bigNumber & EXPONENT_MAX_DEBT_FACTOR) + diff;\n if (result > PRECISION) {\n result = (_resultNumerator << EXPONENT_SIZE_DEBT_FACTOR) + result - PRECISION; // divides by TWO_POWER_64 by reducing exponent by 64\n } else {\n // if number1 is small, e.g. 1e4 and bigNumber is also small e.g. coefficient = 17179869184 & exponent is at 50\n // then: resultNumerator = 171798691840000, diff most significant bit = 48, ending up with diff = 13\n // for exponent in result we end up doing: 50 + 13 - 64 -> underflowing exponent.\n // this should never happen anyway, but if it does better to revert than to continue with unknown effects.\n revert(); // debt factor should never become a BigNumber with exponent <= 0\n }\n }\n }\n\n /// @dev multiplies a `bigNumber1` with another `bigNumber2`.\n /// @dev For vault's use case (calculating connection factor of merged branches userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor):\n /// - bigNumbers must have have exponent size 15 bits and be >= 1 & <= 32767\n /// - bigNumber must have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)\n /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.\n /// @dev sum of exponents from `bigNumber1` `bigNumber2` should be > 16384.\n /// e.g. res = bigNumber1 * bigNumber2 = [(coe1, exp1) * (coe2, exp2)] >> decimal\n /// = (coe1*coe2>>overflow, exp1+exp2+overflow-decimal)\n /// @param bigNumber1 BigNumber format with coefficient and exponent.\n /// @param bigNumber2 BigNumber format with coefficient and exponent.\n /// @return BigNumber format with coefficient and exponent\n function mulBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {\n unchecked {\n // coefficient1_ * coefficient2_\n uint resCoefficient_ = (bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) *\n (bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR);\n // res coefficient at min can be 17179869184 * 17179869184 = 295147905179352825856 (= 1 << 68; 69th bit as 1)\n // res coefficient at max can be 34359738367 * 34359738367 = 1180591620648691826689 (X35 * X35 fits in 70 bits)\n uint overflowLen_ = resCoefficient_ > TWO_POWER_69_MINUS_1\n ? COEFFICIENT_SIZE_DEBT_FACTOR\n : COEFFICIENT_SIZE_DEBT_FACTOR - 1;\n // overflowLen_ is either 34 or 35\n resCoefficient_ = resCoefficient_ >> overflowLen_;\n\n // bigNumber2 is connection factor\n // exponent1_ + exponent2_ + overflowLen_ - decimals\n uint resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) +\n (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) +\n overflowLen_);\n if (resExponent_ < DECIMALS_DEBT_FACTOR) {\n // for this ever to happen, the debt factors used to calculate connection factors would have to be at extremely\n // unrealistic values. Like e.g.\n // branch3 (debt factor X35 << 15 | 16383) got merged into branch2 (debt factor X35 << 15 | 8190)\n // -> connection factor (divBigNumber): ((coe1<>overflowLen, exp1+decimal+overflowLen-exp2-precision_) so:\n // coefficient: (X35<<64)/X35 >> 30 = 17179869184\n // exponent: 8190+16384+30-16383-64 = 8157.\n // result: 17179869184 << 15 | 8157\n // and then branch2 into branch1 (debt factor X35 << 15 | 22). -> connection factor:\n // coefficient: (X35<<64)/X35 >> 30 = 17179869184\n // exponent: 22+16384+30-8190-64 = 8182.\n // result: 17179869184 << 15 | 8182\n // connection factors sum up (mulBigNumber): (coe1*coe2>>overflow, exp1+exp2+overflow-decimal)\n // exponent: 8182+8157+35-16384=16374-16384=-10. underflow.\n // this should never happen anyway, but if it does better to revert than to continue with unknown effects.\n revert();\n }\n resExponent_ = resExponent_ - DECIMALS_DEBT_FACTOR;\n\n if (resExponent_ > EXPONENT_MAX_DEBT_FACTOR) {\n // if resExponent_ is not within limits that means user's got ~100% (something like 99.999999999999...)\n // this situation will probably never happen and this basically means user's position is ~100% liquidated\n return MAX_MASK_DEBT_FACTOR;\n }\n\n return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_);\n }\n }\n\n /// @dev divides a `bigNumber1` by `bigNumber2`.\n /// @dev For vault's use case (calculating connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor) bigNumbers MUST always:\n /// - have exponent size 15 bits and be >= 1 & <= 16384\n /// - have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)\n /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.\n /// - as a result of previous points, numbers must never be 0\n /// e.g. res = bigNumber1 / bigNumber2 = [(coe1, exp1) / (coe2, exp2)] << decimal\n /// = ((coe1<= baseBranchDebtFactor (c = x*100/y with both x,y > 0 & x,y <= 100: c can only ever be >= x)\n function divBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {\n unchecked {\n // (coefficient1_ << PRECISION) / coefficient2_\n uint256 resCoefficient_ = ((bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) << PRECISION) /\n (bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR);\n // nominator at min 17179869184 << 64 = 316912650057057350374175801344. at max 34359738367 << 64 = 633825300095667956674642051072.\n // so min value resCoefficient_ 9223372037123211264 (64 bits) vs max 36893488146345361408 (fits in 65 bits)\n\n // mostSigBit will be PRECISION + 1 or PRECISION\n uint256 overflowLen_ = ((resCoefficient_ >> PRECISION) == 1) ? (PRECISION + 1) : PRECISION;\n // Overflow will be PRECISION - COEFFICIENT_SIZE_DEBT_FACTOR or (PRECISION + 1) - COEFFICIENT_SIZE_DEBT_FACTOR\n // Meaning 64 - 35 = 29 or 65 - 35 = 30\n overflowLen_ = overflowLen_ - COEFFICIENT_SIZE_DEBT_FACTOR;\n resCoefficient_ = resCoefficient_ >> overflowLen_;\n\n // exponent1_ will always be less than or equal to 16384\n // exponent2_ will always be less than or equal to 16384\n // Even if exponent2_ is 0 (not possible) & resExponent_ = DECIMALS_DEBT_FACTOR then also resExponent_ will be less than max limit, so no overflow\n // result exponent = (exponent1_ + DECIMALS_DEBT_FACTOR + overflowLen_) - (exponent2_ + PRECISION);\n uint256 resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) + // exponent1_\n DECIMALS_DEBT_FACTOR + // DECIMALS_DEBT_FACTOR is 100% as it is percentage value\n overflowLen_); // addition part resExponent_ here min 16414, max 32798\n // reuse overFlowLen_ variable for subtraction sum of exponent\n overflowLen_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) + PRECISION; // subtraction part overflowLen_ here: min 65, max 16448\n if (resExponent_ > overflowLen_) {\n resExponent_ = resExponent_ - overflowLen_;\n\n return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_);\n }\n\n // Can happen if bigNumber1 exponent is < 35 (35+16384+29 = 16448) and bigNumber2 exponent is e.g. max 16384.\n // this would mean a branch with a normal big debt factor (bigNumber2) is merged into a base branch with an extremely small\n // debt factor (bigNumber1).\n // this should never happen anyway, but if it does better to revert than to continue with unknown effects.\n revert(); // connection factor should never become a BigNumber with exponent <= 0\n }\n }\n}\n" }, "contracts/libraries/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary LibsErrorTypes {\n /***********************************|\n | LiquidityCalcs | \n |__________________________________*/\n\n /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)\n uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;\n\n /// @notice thrown when rate data is set to a version that is not implemented\n uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;\n\n /// @notice thrown when the calculated borrow rate turns negative. This should never happen.\n uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003;\n\n /***********************************|\n | SafeTransfer | \n |__________________________________*/\n\n /// @notice thrown when safe transfer from for an ERC20 fails\n uint256 internal constant SafeTransfer__TransferFromFailed = 71001;\n\n /// @notice thrown when safe transfer for an ERC20 fails\n uint256 internal constant SafeTransfer__TransferFailed = 71002;\n}\n" }, "contracts/libraries/liquidityCalcs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { LibsErrorTypes as ErrorTypes } from \"./errorTypes.sol\";\nimport { LiquiditySlotsLink } from \"./liquiditySlotsLink.sol\";\nimport { BigMathMinified } from \"./bigMathMinified.sol\";\n\n/// @notice implements calculation methods used for Fluid liquidity such as updated exchange prices,\n/// borrow rate, withdrawal / borrow limits, revenue amount.\nlibrary LiquidityCalcs {\n error FluidLiquidityCalcsError(uint256 errorId_);\n\n /// @notice emitted if the calculated borrow rate surpassed max borrow rate (16 bits) and was capped at maximum value 65535\n event BorrowRateMaxCap();\n\n /// @dev constants as from Liquidity variables.sol\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n // constants used for BigMath conversion from and to storage\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;\n\n uint256 internal constant FOUR_DECIMALS = 1e4;\n uint256 internal constant TWELVE_DECIMALS = 1e12;\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X15 = 0x7fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n uint256 internal constant X33 = 0x1ffffffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC EXCHANGE PRICES /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage.\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @return supplyExchangePrice_ updated supplyExchangePrice\n /// @return borrowExchangePrice_ updated borrowExchangePrice\n function calcExchangePrices(\n uint256 exchangePricesAndConfig_\n ) internal view returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n // Extracting exchange prices\n supplyExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &\n X64;\n borrowExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &\n X64;\n\n if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__ExchangePriceZero);\n }\n\n uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate\n\n unchecked {\n // last timestamp can not be > current timestamp\n uint256 secondsSinceLastUpdate_ = block.timestamp -\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33);\n\n uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) &\n X15;\n if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) {\n // if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed\n // (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // calculate new borrow exchange price.\n // formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_.\n // nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0.\n borrowExchangePrice_ +=\n (borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS);\n\n // FOR SUPPLY EXCHANGE PRICE:\n // all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest.\n // formula: previous price * supply rate * secondsSinceLastUpdate_.\n // where supply rate = (borrow rate - revenueFee%) * ratioSupplyYield. And\n // ratioSupplyYield = utilization * supplyRatio * borrowRatio\n //\n // Example:\n // supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50.\n // BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%.\n // yield is 10 (so half a year must have passed).\n // supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60.\n // borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70.\n // supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2).\n // utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield).\n // borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield):\n // x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%.\n // ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5%\n // supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5%\n // increase in supplyExchangePrice, assuming 100 as previous price.\n // 100 * 22,5% * 1/2 (half a year) = 0,1125.\n // cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20.\n\n // -------------- 1. calculate ratioSupplyYield --------------------------------\n // step1: utilization * supplyRatio (or actually part of lenders receiving yield)\n\n // temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n // if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n // else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15;\n\n if (temp_ == 1) {\n // if no raw supply: no exchange price update needed\n // (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest\n if (temp_ & 1 == 1) {\n // ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = temp_ >> 1;\n\n // Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return\n // in the if statement a little above.\n\n // based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee.\n // supplyRawInterest must become worth 30. totalSupply must become 110.\n // supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2.\n // so ratioSupplyYield must come out as 2.5 (250%).\n // supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted.\n temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision)\n // e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%).\n temp_ =\n // utilization * (100% + 100% / supplyRatio)\n (((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) *\n (1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS);\n // max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31\n } else {\n // ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n temp_ = temp_ >> 1;\n // if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0\n\n // e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%).\n temp_ =\n // 1e27 * utilization * (100% + supplyRatio) / 100%\n (1e27 *\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS + temp_)) /\n (FOUR_DECIMALS * FOUR_DECIMALS);\n // max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27\n }\n // from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31\n\n // step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield)\n if (borrowRatio_ & 1 == 1) {\n // ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n\n // Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered\n // at the beginning of the method by early return if `borrowRatio_ == 1`.\n\n // based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%.\n // so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed.\n // x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666%\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_);\n // max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield).\n } else {\n // ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n // x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%.\n borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_)));\n // borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%.\n // or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101.\n // max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield).\n }\n\n // temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%.\n // max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8\n temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54;\n\n // 2. calculate supply rate\n // temp_ => supply rate (borrow rate - revenueFee%) * ratioSupplyYield.\n // division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate)\n // Note that all calculation divisions for supplyExchangePrice are rounded down.\n // Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest\n // but more suppliers not earning interest.\n temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate\n temp_ * // ratioSupplyYield\n (FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee\n // fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17.\n\n // 3. calculate increase in supply exchange price\n supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS));\n // max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0.\n }\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC REVENUE /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage\n /// and the current balance of the Fluid liquidity contract for the token.\n /// @param totalAmounts_ total amounts packed uint256 read from storage\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))\n /// @return revenueAmount_ collectable revenue amount\n function calcRevenue(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_\n ) internal view returns (uint256 revenueAmount_) {\n // @dev no need to super-optimize this method as it is only used by admin\n\n // calculate the new exchange prices based on earned interest\n (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(exchangePricesAndConfig_);\n\n // total supply = interest free + with interest converted from raw\n uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_);\n\n if (totalSupply_ > 0) {\n // available revenue: balanceOf(token) + totalBorrowings - totalLendings.\n revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_);\n // ensure there is no possible case because of rounding etc. where this would revert,\n // explicitly check if >\n revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0;\n // Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization\n // can only be revenue.\n } else {\n // if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck\n revenueAmount_ = liquidityTokenBalance_;\n }\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC LIMITS /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev calculates withdrawal limit before an operate execution:\n /// amount of user supply that must stay supplied (not amount that can be withdrawn).\n /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M\n /// @param userSupplyData_ user supply data packed uint256 from storage\n /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath\n /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction.\n /// returned value is in raw for with interest mode, normal amount for interest free mode!\n function calcWithdrawalLimitBeforeOperate(\n uint256 userSupplyData_,\n uint256 userSupply_\n ) internal view returns (uint256 currentWithdrawalLimit_) {\n // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet).\n // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet.\n // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be\n // a deposit anyway. Important is that it would not revert.\n\n // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit\n // is the fully expanded limit immediately.\n\n // extract last set withdrawal limit\n uint256 lastWithdrawalLimit_ = (userSupplyData_ >>\n LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) & X64;\n lastWithdrawalLimit_ =\n (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) <<\n (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK);\n if (lastWithdrawalLimit_ == 0) {\n // withdrawal limit is not activated. Max withdrawal allowed\n return 0;\n }\n\n uint256 maxWithdrawableLimit_;\n uint256 temp_;\n unchecked {\n // extract max withdrawable percent of user supply and\n // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed\n // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed.\n\n // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n maxWithdrawableLimit_ =\n (((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) /\n FOUR_DECIMALS;\n\n // time elapsed since last withdrawal limit was set (in seconds)\n // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before.\n // last timestamp can not be > current timestamp\n temp_ =\n block.timestamp -\n ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33);\n }\n // calculate withdrawable amount of expandPercent that is elapsed of expandDuration.\n // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%.\n // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed.\n temp_ =\n (maxWithdrawableLimit_ * temp_) /\n // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit)\n ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0\n // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount.\n // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration,\n // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0\n // which will cause minimum (fully expanded) withdrawal limit to be set in lines below.\n unchecked {\n // underflow explicitly checked & handled\n currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0;\n // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion.\n // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_\n temp_ = userSupply_ - maxWithdrawableLimit_;\n }\n // if withdrawal limit is decreased below minimum then set minimum\n // (e.g. when more than expandDuration time has elapsed)\n if (temp_ > currentWithdrawalLimit_) {\n currentWithdrawalLimit_ = temp_;\n }\n }\n\n /// @dev calculates withdrawal limit after an operate execution:\n /// amount of user supply that must stay supplied (not amount that can be withdrawn).\n /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M\n /// @param userSupplyData_ user supply data packed uint256 from storage\n /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount\n /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate`\n /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in\n /// raw for with interest mode, normal amount for interest free mode!\n function calcWithdrawalLimitAfterOperate(\n uint256 userSupplyData_,\n uint256 userSupply_,\n uint256 newWithdrawalLimit_\n ) internal pure returns (uint256) {\n // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed\n uint256 temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n // if user supply is below base limit then max withdrawals are allowed\n if (userSupply_ < temp_) {\n return 0;\n }\n // temp_ => withdrawal limit expandPercent (is in 1e2 decimals)\n temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;\n unchecked {\n // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent))\n // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_\n temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS);\n }\n // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit.\n // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where\n // increased deposit amount outpaces withrawals.\n if (temp_ > newWithdrawalLimit_) {\n return temp_;\n }\n return newWithdrawalLimit_;\n }\n\n /// @dev calculates borrow limit before an operate execution:\n /// total amount user borrow can reach (not borrowable amount in current operation).\n /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M\n /// @param userBorrowData_ user borrow data packed uint256 from storage\n /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_`\n /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in\n /// raw for with interest mode, normal amount for interest free mode!\n function calcBorrowLimitBeforeOperate(\n uint256 userBorrowData_,\n uint256 userBorrow_\n ) internal view returns (uint256 currentBorrowLimit_) {\n // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit.\n // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus\n // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0.\n\n // temp_ = extract borrow expand percent (is in 1e2 decimals)\n uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;\n\n uint256 maxExpansionLimit_;\n uint256 maxExpandedBorrowLimit_;\n unchecked {\n // calculate max expansion limit: Max amount limit can expand to since last interaction\n // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS);\n\n // calculate max borrow limit: Max point limit can increase to since last interaction\n maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_;\n }\n\n // currentBorrowLimit_ = extract base borrow limit\n currentBorrowLimit_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;\n currentBorrowLimit_ =\n (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) <<\n (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK);\n\n if (maxExpandedBorrowLimit_ < currentBorrowLimit_) {\n return currentBorrowLimit_;\n }\n // time elapsed since last borrow limit was set (in seconds)\n unchecked {\n // temp_ = timeElapsed_ (last timestamp can not be > current timestamp)\n temp_ =\n block.timestamp -\n ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last update timestamp\n }\n\n // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit\n currentBorrowLimit_ =\n // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`.\n // divisor is extract expand duration (after this, full expansion to expandPercentage happened).\n ((maxExpansionLimit_ * temp_) /\n ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0\n // extract last set borrow limit\n BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion,\n // so set to `maxExpandedBorrowLimit_` in that case.\n // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big)\n if (currentBorrowLimit_ > maxExpandedBorrowLimit_) {\n currentBorrowLimit_ = maxExpandedBorrowLimit_;\n }\n // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n if (currentBorrowLimit_ > temp_) {\n currentBorrowLimit_ = temp_;\n }\n }\n\n /// @dev calculates borrow limit after an operate execution:\n /// total amount user borrow can reach (not borrowable amount in current operation).\n /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M\n /// @param userBorrowData_ user borrow data packed uint256 from storage\n /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount\n /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate`\n /// @return borrowLimit_ updated borrow limit that should be written to storage.\n /// returned value is in raw for with interest mode, normal amount for interest free mode!\n function calcBorrowLimitAfterOperate(\n uint256 userBorrowData_,\n uint256 userBorrow_,\n uint256 newBorrowLimit_\n ) internal pure returns (uint256 borrowLimit_) {\n // temp_ = extract borrow expand percent\n uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals)\n\n unchecked {\n // borrowLimit_ = calculate maximum borrow limit at full expansion.\n // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS);\n }\n\n // temp_ = extract base borrow limit\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n if (borrowLimit_ < temp_) {\n // below base limit, borrow limit is always base limit\n return temp_;\n }\n // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n // make sure fully expanded borrow limit is not above hard max borrow limit\n if (borrowLimit_ > temp_) {\n borrowLimit_ = temp_;\n }\n // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit.\n // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)\n if (newBorrowLimit_ > borrowLimit_) {\n return borrowLimit_;\n }\n return newBorrowLimit_;\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC RATES /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev Calculates new borrow rate from utilization for a token\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ totalBorrow / totalSupply. 1e4 = 100% utilization\n /// @return rate_ rate for that particular token in 1e2 precision (e.g. 5% rate = 500)\n function calcBorrowRateFromUtilization(uint256 rateData_, uint256 utilization_) internal returns (uint256 rate_) {\n // extract rate version: 4 bits (0xF) starting from bit 0\n uint256 rateVersion_ = (rateData_ & 0xF);\n\n if (rateVersion_ == 1) {\n rate_ = calcRateV1(rateData_, utilization_);\n } else if (rateVersion_ == 2) {\n rate_ = calcRateV2(rateData_, utilization_);\n } else {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__UnsupportedRateVersion);\n }\n\n if (rate_ > X16) {\n // hard cap for borrow rate at maximum value 16 bits (65535) to make sure it does not overflow storage space.\n // this is unlikely to ever happen if configs stay within expected levels.\n rate_ = X16;\n // emit event to more easily become aware\n emit BorrowRateMaxCap();\n }\n }\n\n /// @dev calculates the borrow rate based on utilization for rate data version 1 (with one kink) in 1e2 precision\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ in 1e2 (100% = 1e4)\n /// @return rate_ rate in 1e2 precision\n function calcRateV1(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {\n /// For rate v1 (one kink) ------------------------------------------------------\n /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 52- 67 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Last 188 bits => 68-255 => blank, might come in use in future\n\n // y = mx + c.\n // y is borrow rate\n // x is utilization\n // m = slope (m can also be negative for declining rates)\n // c is constant (c can be negative)\n\n uint256 y1_;\n uint256 y2_;\n uint256 x1_;\n uint256 x2_;\n\n // extract kink1: 16 bits (0xFFFF) starting from bit 20\n // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two\n uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16;\n if (utilization_ < kink1_) {\n // if utilization is less than kink\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;\n x1_ = 0; // 0%\n x2_ = kink1_;\n } else {\n // else utilization is greater than kink\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) & X16;\n x1_ = kink1_;\n x2_ = FOUR_DECIMALS; // 100%\n }\n\n int256 constant_;\n int256 slope_;\n unchecked {\n // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).\n // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor)\n // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS\n slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_));\n\n // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.\n // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256\n // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;\n // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256\n // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256\n constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_));\n\n // calculating new borrow rate\n // - slope_ max value is 65535 * 1e12,\n // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)\n // - constant max value is 65535 * 1e12\n // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256\n // divisor TWELVE_DECIMALS can not be 0\n slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings\n if (slope_ < 0) {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative);\n }\n rate_ = uint256(slope_) / TWELVE_DECIMALS;\n }\n }\n\n /// @dev calculates the borrow rate based on utilization for rate data version 2 (with two kinks) in 1e4 precision\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ in 1e2 (100% = 1e4)\n /// @return rate_ rate in 1e4 precision\n function calcRateV2(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {\n /// For rate v2 (two kinks) -----------------------------------------------------\n /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 52- 67 => Utilization at kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 68- 83 => Rate at utilization kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 84- 99 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Last 156 bits => 100-255 => blank, might come in use in future\n\n // y = mx + c.\n // y is borrow rate\n // x is utilization\n // m = slope (m can also be negative for declining rates)\n // c is constant (c can be negative)\n\n uint256 y1_;\n uint256 y2_;\n uint256 x1_;\n uint256 x2_;\n\n // extract kink1: 16 bits (0xFFFF) starting from bit 20\n // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two\n uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) & X16;\n if (utilization_ < kink1_) {\n // if utilization is less than kink1\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;\n x1_ = 0; // 0%\n x2_ = kink1_;\n } else {\n // extract kink2: 16 bits (0xFFFF) starting from bit 52\n uint256 kink2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) & X16;\n if (utilization_ < kink2_) {\n // if utilization is less than kink2\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;\n x1_ = kink1_;\n x2_ = kink2_;\n } else {\n // else utilization is greater than kink2\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) & X16;\n x1_ = kink2_;\n x2_ = FOUR_DECIMALS;\n }\n }\n\n int256 constant_;\n int256 slope_;\n unchecked {\n // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).\n // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor)\n // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS\n slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_));\n\n // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.\n // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256\n // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;\n // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256\n // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256\n constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_));\n\n // calculating new borrow rate\n // - slope_ max value is 65535 * 1e12,\n // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)\n // - constant max value is 65535 * 1e12\n // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256\n // divisor TWELVE_DECIMALS can not be 0\n slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings\n if (slope_ < 0) {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative);\n }\n rate_ = uint256(slope_) / TWELVE_DECIMALS;\n }\n }\n\n /// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_`\n function getTotalSupply(\n uint256 totalAmounts_,\n uint256 supplyExchangePrice_\n ) internal pure returns (uint256 totalSupply_) {\n // totalSupply_ => supplyInterestFree\n totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;\n totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits\n totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalSupply = supplyInterestFree + supplyRawInterest normalized from raw\n totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n\n /// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_`\n function getTotalBorrow(\n uint256 totalAmounts_,\n uint256 borrowExchangePrice_\n ) internal pure returns (uint256 totalBorrow_) {\n // totalBorrow_ => borrowInterestFree\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);\n totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;\n totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw\n totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n}\n" }, "contracts/libraries/liquiditySlotsLink.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @notice library that helps in reading / working with storage slot data of Fluid Liquidity.\n/// @dev as all data for Fluid Liquidity is internal, any data must be fetched directly through manual\n/// slot reading through this library or, if gas usage is less important, through the FluidLiquidityResolver.\nlibrary LiquiditySlotsLink {\n /// @dev storage slot for status at Liquidity\n uint256 internal constant LIQUIDITY_STATUS_SLOT = 1;\n /// @dev storage slot for auths mapping at Liquidity\n uint256 internal constant LIQUIDITY_AUTHS_MAPPING_SLOT = 2;\n /// @dev storage slot for guardians mapping at Liquidity\n uint256 internal constant LIQUIDITY_GUARDIANS_MAPPING_SLOT = 3;\n /// @dev storage slot for user class mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_CLASS_MAPPING_SLOT = 4;\n /// @dev storage slot for exchangePricesAndConfig mapping at Liquidity\n uint256 internal constant LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT = 5;\n /// @dev storage slot for rateData mapping at Liquidity\n uint256 internal constant LIQUIDITY_RATE_DATA_MAPPING_SLOT = 6;\n /// @dev storage slot for totalAmounts mapping at Liquidity\n uint256 internal constant LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT = 7;\n /// @dev storage slot for user supply double mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT = 8;\n /// @dev storage slot for user borrow double mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT = 9;\n /// @dev storage slot for listed tokens array at Liquidity\n uint256 internal constant LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT = 10;\n /// @dev storage slot for listed tokens array at Liquidity\n uint256 internal constant LIQUIDITY_CONFIGS2_MAPPING_SLOT = 11;\n\n // --------------------------------\n // @dev stacked uint256 storage slots bits position data for each:\n\n // ExchangePricesAndConfig\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATE = 0;\n uint256 internal constant BITS_EXCHANGE_PRICES_FEE = 16;\n uint256 internal constant BITS_EXCHANGE_PRICES_UTILIZATION = 30;\n uint256 internal constant BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD = 44;\n uint256 internal constant BITS_EXCHANGE_PRICES_LAST_TIMESTAMP = 58;\n uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE = 91;\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE = 155;\n uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_RATIO = 219;\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATIO = 234;\n uint256 internal constant BITS_EXCHANGE_PRICES_USES_CONFIGS2 = 249;\n\n // RateData:\n uint256 internal constant BITS_RATE_DATA_VERSION = 0;\n // RateData: V1\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO = 4;\n uint256 internal constant BITS_RATE_DATA_V1_UTILIZATION_AT_KINK = 20;\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK = 36;\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX = 52;\n // RateData: V2\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO = 4;\n uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1 = 20;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1 = 36;\n uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2 = 52;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2 = 68;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX = 84;\n\n // TotalAmounts\n uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_WITH_INTEREST = 0;\n uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE = 64;\n uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST = 128;\n uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE = 192;\n\n // UserSupplyData\n uint256 internal constant BITS_USER_SUPPLY_MODE = 0;\n uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;\n uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;\n uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;\n uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;\n uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;\n uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;\n uint256 internal constant BITS_USER_SUPPLY_IS_PAUSED = 255;\n\n // UserBorrowData\n uint256 internal constant BITS_USER_BORROW_MODE = 0;\n uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;\n uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;\n uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;\n uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;\n uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;\n uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;\n uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;\n uint256 internal constant BITS_USER_BORROW_IS_PAUSED = 255;\n\n // Configs2\n uint256 internal constant BITS_CONFIGS2_MAX_UTILIZATION = 0;\n\n // --------------------------------\n\n /// @notice Calculating the slot ID for Liquidity contract for single mapping at `slot_` for `key_`\n function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for double mapping at `slot_` for `key1_` and `key2_`\n function calculateDoubleMappingStorageSlot(\n uint256 slot_,\n address key1_,\n address key2_\n ) internal pure returns (bytes32) {\n bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));\n return keccak256(abi.encode(key2_, intermediateSlot_));\n }\n}\n" }, "contracts/libraries/safeTransfer.sol": { "content": "// SPDX-License-Identifier: MIT OR Apache-2.0\npragma solidity 0.8.21;\n\nimport { LibsErrorTypes as ErrorTypes } from \"./errorTypes.sol\";\n\n/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom\nlibrary SafeTransfer {\n error FluidSafeTransferError(uint256 errorId_);\n\n /// @dev Transfer `amount_` of `token_` from `from_` to `to_`, spending the approval given by `from_` to the\n /// calling contract. If `token_` returns no value, non-reverting calls are assumed to be successful.\n /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63\n function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"from_\" argument.\n mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"to_\" argument.\n mstore(add(freeMemoryPointer, 68), amount_) // Append the \"amount_\" argument. Masking not required as it's a full 32 byte type.\n\n success_ := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)\n )\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);\n }\n }\n\n /// @dev Transfer `amount_` of `token_` to `to_`.\n /// If `token_` returns no value, non-reverting calls are assumed to be successful.\n /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95\n function safeTransfer(address token_, address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"to_\" argument.\n mstore(add(freeMemoryPointer, 36), amount_) // Append the \"amount_\" argument. Masking not required as it's a full 32 byte type.\n\n success_ := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);\n }\n }\n\n /// @dev Transfer `amount_` of ` native token to `to_`.\n /// Minimally modified from Solmate SafeTransferLib (Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25\n function safeTransferNative(address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Transfer the ETH and store if it succeeded or not.\n success_ := call(gas(), to_, amount_, 0, 0, 0, 0)\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);\n }\n }\n}\n" }, "contracts/libraries/storageRead.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @notice implements a method to read uint256 data from storage at a bytes32 storage slot key.\ncontract StorageRead {\n function readFromStorage(bytes32 slot_) public view returns (uint256 result_) {\n assembly {\n result_ := sload(slot_) // read value from the storage slot\n }\n }\n}\n" }, "contracts/libraries/tickMath.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title library that calculates number \"tick\" and \"ratioX96\" from this: ratioX96 = (1.0015^tick) * 2^96\n/// @notice this library is used in Fluid Vault protocol for optimiziation.\n/// @dev \"tick\" supports between -32767 and 32767. \"ratioX96\" supports between 37075072 and 169307877264527972847801929085841449095838922544595\nlibrary TickMath {\n /// The minimum tick that can be passed in getRatioAtTick. 1.0015**-32767\n int24 internal constant MIN_TICK = -32767;\n /// The maximum tick that can be passed in getRatioAtTick. 1.0015**32767\n int24 internal constant MAX_TICK = 32767;\n\n uint256 internal constant FACTOR00 = 0x100000000000000000000000000000000;\n uint256 internal constant FACTOR01 = 0xff9dd7de423466c20352b1246ce4856f; // 2^128/1.0015**1 = 339772707859149738855091969477551883631\n uint256 internal constant FACTOR02 = 0xff3bd55f4488ad277531fa1c725a66d0; // 2^128/1.0015**2 = 339263812140938331358054887146831636176\n uint256 internal constant FACTOR03 = 0xfe78410fd6498b73cb96a6917f853259; // 2^128/1.0015**4 = 338248306163758188337119769319392490073\n uint256 internal constant FACTOR04 = 0xfcf2d9987c9be178ad5bfeffaa123273; // 2^128/1.0015**8 = 336226404141693512316971918999264834163\n uint256 internal constant FACTOR05 = 0xf9ef02c4529258b057769680fc6601b3; // 2^128/1.0015**16 = 332218786018727629051611634067491389875\n uint256 internal constant FACTOR06 = 0xf402d288133a85a17784a411f7aba082; // 2^128/1.0015**32 = 324346285652234375371948336458280706178\n uint256 internal constant FACTOR07 = 0xe895615b5beb6386553757b0352bda90; // 2^128/1.0015**64 = 309156521885964218294057947947195947664\n uint256 internal constant FACTOR08 = 0xd34f17a00ffa00a8309940a15930391a; // 2^128/1.0015**128 = 280877777739312896540849703637713172762 \n uint256 internal constant FACTOR09 = 0xae6b7961714e20548d88ea5123f9a0ff; // 2^128/1.0015**256 = 231843708922198649176471782639349113087\n uint256 internal constant FACTOR10 = 0x76d6461f27082d74e0feed3b388c0ca1; // 2^128/1.0015**512 = 157961477267171621126394973980180876449\n uint256 internal constant FACTOR11 = 0x372a3bfe0745d8b6b19d985d9a8b85bb; // 2^128/1.0015**1024 = 73326833024599564193373530205717235131\n uint256 internal constant FACTOR12 = 0x0be32cbee48979763cf7247dd7bb539d; // 2^128/1.0015**2048 = 15801066890623697521348224657638773661\n uint256 internal constant FACTOR13 = 0x8d4f70c9ff4924dac37612d1e2921e; // 2^128/1.0015**4096 = 733725103481409245883800626999235102\n uint256 internal constant FACTOR14 = 0x4e009ae5519380809a02ca7aec77; // 2^128/1.0015**8192 = 1582075887005588088019997442108535\n uint256 internal constant FACTOR15 = 0x17c45e641b6e95dee056ff10; // 2^128/1.0015**16384 = 7355550435635883087458926352\n\n /// The minimum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MIN_TICK). ~ Equivalent to `(1 << 96) * (1.0015**-32767)`\n uint256 internal constant MIN_RATIOX96 = 37075072;\n /// The maximum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MAX_TICK).\n /// ~ Equivalent to `(1 << 96) * (1.0015**32767)`, rounding etc. leading to minor difference\n uint256 internal constant MAX_RATIOX96 = 169307877264527972847801929085841449095838922544595;\n\n uint256 internal constant ZERO_TICK_SCALED_RATIO = 0x1000000000000000000000000; // 1 << 96 // 79228162514264337593543950336\n uint256 internal constant _1E26 = 1e26;\n\n /// @notice ratioX96 = (1.0015^tick) * 2^96\n /// @dev Throws if |tick| > max tick\n /// @param tick The input tick for the above formula\n /// @return ratioX96 ratio = (debt amount/collateral amount)\n function getRatioAtTick(int tick) internal pure returns (uint256 ratioX96) {\n assembly {\n let absTick_ := sub(xor(tick, sar(255, tick)), sar(255, tick))\n\n if gt(absTick_, MAX_TICK) {\n revert(0, 0)\n }\n let factor_ := FACTOR00\n if and(absTick_, 0x1) {\n factor_ := FACTOR01\n }\n if and(absTick_, 0x2) {\n factor_ := shr(128, mul(factor_, FACTOR02))\n }\n if and(absTick_, 0x4) {\n factor_ := shr(128, mul(factor_, FACTOR03))\n }\n if and(absTick_, 0x8) {\n factor_ := shr(128, mul(factor_, FACTOR04))\n }\n if and(absTick_, 0x10) {\n factor_ := shr(128, mul(factor_, FACTOR05))\n }\n if and(absTick_, 0x20) {\n factor_ := shr(128, mul(factor_, FACTOR06))\n }\n if and(absTick_, 0x40) {\n factor_ := shr(128, mul(factor_, FACTOR07))\n }\n if and(absTick_, 0x80) {\n factor_ := shr(128, mul(factor_, FACTOR08))\n }\n if and(absTick_, 0x100) {\n factor_ := shr(128, mul(factor_, FACTOR09))\n }\n if and(absTick_, 0x200) {\n factor_ := shr(128, mul(factor_, FACTOR10))\n }\n if and(absTick_, 0x400) {\n factor_ := shr(128, mul(factor_, FACTOR11))\n }\n if and(absTick_, 0x800) {\n factor_ := shr(128, mul(factor_, FACTOR12))\n }\n if and(absTick_, 0x1000) {\n factor_ := shr(128, mul(factor_, FACTOR13))\n }\n if and(absTick_, 0x2000) {\n factor_ := shr(128, mul(factor_, FACTOR14))\n }\n if and(absTick_, 0x4000) {\n factor_ := shr(128, mul(factor_, FACTOR15))\n }\n\n let precision_ := 0\n if iszero(and(tick, 0x8000000000000000000000000000000000000000000000000000000000000000)) {\n factor_ := div(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, factor_)\n // we round up in the division so getTickAtRatio of the output price is always consistent\n if mod(factor_, 0x100000000) {\n precision_ := 1\n }\n }\n ratioX96 := add(shr(32, factor_), precision_)\n }\n }\n\n /// @notice ratioX96 = (1.0015^tick) * 2^96\n /// @dev Throws if ratioX96 > max ratio || ratioX96 < min ratio\n /// @param ratioX96 The input ratio; ratio = (debt amount/collateral amount)\n /// @return tick The output tick for the above formula. Returns in round down form. if tick is 123.23 then 123, if tick is -123.23 then returns -124\n /// @return perfectRatioX96 perfect ratio for the above tick\n function getTickAtRatio(uint256 ratioX96) internal pure returns (int tick, uint perfectRatioX96) {\n assembly {\n if or(gt(ratioX96, MAX_RATIOX96), lt(ratioX96, MIN_RATIOX96)) {\n revert(0, 0)\n }\n\n let cond := lt(ratioX96, ZERO_TICK_SCALED_RATIO)\n let factor_\n\n if iszero(cond) {\n // if ratioX96 >= ZERO_TICK_SCALED_RATIO\n factor_ := div(mul(ratioX96, _1E26), ZERO_TICK_SCALED_RATIO)\n }\n if cond {\n // ratioX96 < ZERO_TICK_SCALED_RATIO\n factor_ := div(mul(ZERO_TICK_SCALED_RATIO, _1E26), ratioX96)\n }\n\n // put in https://www.wolframalpha.com/ whole equation: (1.0015^tick) * 2^96 * 10^26 / 79228162514264337593543950336\n\n // for tick = 16384\n // ratioX96 = (1.0015^16384) * 2^96 = 3665252098134783297721995888537077351735\n // 3665252098134783297721995888537077351735 * 10^26 / 79228162514264337593543950336 =\n // 4626198540796508716348404308345255985.06131964639489434655721\n if iszero(lt(factor_, 4626198540796508716348404308345255985)) {\n tick := or(tick, 0x4000)\n factor_ := div(mul(factor_, _1E26), 4626198540796508716348404308345255985)\n }\n // for tick = 8192\n // ratioX96 = (1.0015^8192) * 2^96 = 17040868196391020479062776466509865\n // 17040868196391020479062776466509865 * 10^26 / 79228162514264337593543950336 =\n // 21508599537851153911767490449162.3037648642153898377655505172\n if iszero(lt(factor_, 21508599537851153911767490449162)) {\n tick := or(tick, 0x2000)\n factor_ := div(mul(factor_, _1E26), 21508599537851153911767490449162)\n }\n // for tick = 4096\n // ratioX96 = (1.0015^4096) * 2^96 = 36743933851015821532611831851150\n // 36743933851015821532611831851150 * 10^26 / 79228162514264337593543950336 =\n // 46377364670549310883002866648.9777607649742626173648716941385\n if iszero(lt(factor_, 46377364670549310883002866649)) {\n tick := or(tick, 0x1000)\n factor_ := div(mul(factor_, _1E26), 46377364670549310883002866649)\n }\n // for tick = 2048\n // ratioX96 = (1.0015^2048) * 2^96 = 1706210527034005899209104452335\n // 1706210527034005899209104452335 * 10^26 / 79228162514264337593543950336 =\n // 2153540449365864845468344760.06357108484096046743300420319322\n if iszero(lt(factor_, 2153540449365864845468344760)) {\n tick := or(tick, 0x800)\n factor_ := div(mul(factor_, _1E26), 2153540449365864845468344760)\n }\n // for tick = 1024\n // ratioX96 = (1.0015^1024) * 2^96 = 367668226692760093024536487236\n // 367668226692760093024536487236 * 10^26 / 79228162514264337593543950336 =\n // 464062544207767844008185024.950588990554136265212906454481127\n if iszero(lt(factor_, 464062544207767844008185025)) {\n tick := or(tick, 0x400)\n factor_ := div(mul(factor_, _1E26), 464062544207767844008185025)\n }\n // for tick = 512\n // ratioX96 = (1.0015^512) * 2^96 = 170674186729409605620119663668\n // 170674186729409605620119663668 * 10^26 / 79228162514264337593543950336 =\n // 215421109505955298802281577.031879604792139232258508172947569\n if iszero(lt(factor_, 215421109505955298802281577)) {\n tick := or(tick, 0x200)\n factor_ := div(mul(factor_, _1E26), 215421109505955298802281577)\n }\n // for tick = 256\n // ratioX96 = (1.0015^256) * 2^96 = 116285004205991934861656513301\n // 116285004205991934861656513301 * 10^26 / 79228162514264337593543950336 =\n // 146772309890508740607270614.667650899656438875541505058062410\n if iszero(lt(factor_, 146772309890508740607270615)) {\n tick := or(tick, 0x100)\n factor_ := div(mul(factor_, _1E26), 146772309890508740607270615)\n }\n // for tick = 128\n // ratioX96 = (1.0015^128) * 2^96 = 95984619659632141743747099590\n // 95984619659632141743747099590 * 10^26 / 79228162514264337593543950336 =\n // 121149622323187099817270416.157248837742741760456796835775887\n if iszero(lt(factor_, 121149622323187099817270416)) {\n tick := or(tick, 0x80)\n factor_ := div(mul(factor_, _1E26), 121149622323187099817270416)\n }\n // for tick = 64\n // ratioX96 = (1.0015^64) * 2^96 = 87204845308406958006717891124\n // 87204845308406958006717891124 * 10^26 / 79228162514264337593543950336 =\n // 110067989135437147685980801.568068573422377364214113968609839\n if iszero(lt(factor_, 110067989135437147685980801)) {\n tick := or(tick, 0x40)\n factor_ := div(mul(factor_, _1E26), 110067989135437147685980801)\n }\n // for tick = 32\n // ratioX96 = (1.0015^32) * 2^96 = 83120873769022354029916374475\n // 83120873769022354029916374475 * 10^26 / 79228162514264337593543950336 =\n // 104913292358707887270979599.831816586773651266562785765558183\n if iszero(lt(factor_, 104913292358707887270979600)) {\n tick := or(tick, 0x20)\n factor_ := div(mul(factor_, _1E26), 104913292358707887270979600)\n }\n // for tick = 16\n // ratioX96 = (1.0015^16) * 2^96 = 81151180492336368327184716176\n // 81151180492336368327184716176 * 10^26 / 79228162514264337593543950336 =\n // 102427189924701091191840927.762844039579442328381455567932128\n if iszero(lt(factor_, 102427189924701091191840928)) {\n tick := or(tick, 0x10)\n factor_ := div(mul(factor_, _1E26), 102427189924701091191840928)\n }\n // for tick = 8\n // ratioX96 = (1.0015^8) * 2^96 = 80183906840906820640659903620\n // 80183906840906820640659903620 * 10^26 / 79228162514264337593543950336 =\n // 101206318935480056907421312.890625\n if iszero(lt(factor_, 101206318935480056907421313)) {\n tick := or(tick, 0x8)\n factor_ := div(mul(factor_, _1E26), 101206318935480056907421313)\n }\n // for tick = 4\n // ratioX96 = (1.0015^4) * 2^96 = 79704602139525152702959747603\n // 79704602139525152702959747603 * 10^26 / 79228162514264337593543950336 =\n // 100601351350506250000000000\n if iszero(lt(factor_, 100601351350506250000000000)) {\n tick := or(tick, 0x4)\n factor_ := div(mul(factor_, _1E26), 100601351350506250000000000)\n }\n // for tick = 2\n // ratioX96 = (1.0015^2) * 2^96 = 79466025265172787701084167660\n // 79466025265172787701084167660 * 10^26 / 79228162514264337593543950336 =\n // 100300225000000000000000000\n if iszero(lt(factor_, 100300225000000000000000000)) {\n tick := or(tick, 0x2)\n factor_ := div(mul(factor_, _1E26), 100300225000000000000000000)\n }\n // for tick = 1\n // ratioX96 = (1.0015^1) * 2^96 = 79347004758035734099934266261\n // 79347004758035734099934266261 * 10^26 / 79228162514264337593543950336 =\n // 100150000000000000000000000\n if iszero(lt(factor_, 100150000000000000000000000)) {\n tick := or(tick, 0x1)\n factor_ := div(mul(factor_, _1E26), 100150000000000000000000000)\n }\n if iszero(cond) {\n // if ratioX96 >= ZERO_TICK_SCALED_RATIO\n perfectRatioX96 := div(mul(ratioX96, _1E26), factor_)\n }\n if cond {\n // ratioX96 < ZERO_TICK_SCALED_RATIO\n tick := not(tick)\n perfectRatioX96 := div(mul(ratioX96, factor_), 100150000000000000000000000)\n }\n // perfect ratio should always be <= ratioX96\n // not sure if it can ever be bigger but better to have extra checks\n if gt(perfectRatioX96, ratioX96) {\n revert(0, 0)\n }\n }\n }\n}\n" }, "contracts/liquidity/adminModule/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Structs } from \"./structs.sol\";\n\ncontract Events is Structs {\n /// @notice emitted when allowed auths are updated\n event LogUpdateAuths(AddressBool[] authsStatus);\n\n /// @notice emitted when allowed guardians are updated\n event LogUpdateGuardians(AddressBool[] guardiansStatus);\n\n /// @notice emitted when revenue collector address is updated\n event LogUpdateRevenueCollector(address indexed revenueCollector);\n\n /// @notice emitted when status is changed (paused / unpaused)\n event LogChangeStatus(uint256 indexed newStatus);\n\n /// @notice emitted when user classes are updated\n event LogUpdateUserClasses(AddressUint256[] userClasses);\n\n /// @notice emitted when token configs are updated\n event LogUpdateTokenConfigs(TokenConfig[] tokenConfigs);\n\n /// @notice emitted when user supply configs are updated\n event LogUpdateUserSupplyConfigs(UserSupplyConfig[] userSupplyConfigs);\n\n /// @notice emitted when user borrow configs are updated\n event LogUpdateUserBorrowConfigs(UserBorrowConfig[] userBorrowConfigs);\n\n /// @notice emitted when a user gets certain tokens paused\n event LogPauseUser(address user, address[] supplyTokens, address[] borrowTokens);\n\n /// @notice emitted when a user gets certain tokens unpaused\n event LogUnpauseUser(address user, address[] supplyTokens, address[] borrowTokens);\n\n /// @notice emitted when token rate data is updated with rate data v1\n event LogUpdateRateDataV1s(RateDataV1Params[] tokenRateDatas);\n\n /// @notice emitted when token rate data is updated with rate data v2\n event LogUpdateRateDataV2s(RateDataV2Params[] tokenRateDatas);\n\n /// @notice emitted when revenue is collected\n event LogCollectRevenue(address indexed token, uint256 indexed amount);\n\n /// @notice emitted when exchange prices and borrow rate are updated\n event LogUpdateExchangePrices(\n address indexed token,\n uint256 indexed supplyExchangePrice,\n uint256 indexed borrowExchangePrice,\n uint256 borrowRate,\n uint256 utilization\n );\n}\n" }, "contracts/liquidity/adminModule/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { FixedPointMathLib } from \"solmate/src/utils/FixedPointMathLib.sol\";\n\nimport { BigMathMinified } from \"../../libraries/bigMathMinified.sol\";\nimport { LiquidityCalcs } from \"../../libraries/liquidityCalcs.sol\";\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { Events } from \"./events.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { CommonHelpers } from \"../common/helpers.sol\";\nimport { IFluidLiquidityAdmin } from \"../interfaces/iLiquidity.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\nabstract contract AdminModuleConstants is Error {\n /// @dev hard cap value for max borrow limit, used as sanity check. Usually 10x of total supply.\n uint256 public immutable NATIVE_TOKEN_MAX_BORROW_LIMIT_CAP;\n\n constructor(uint256 nativeTokenMaxBorrowLimitCap_) {\n if (nativeTokenMaxBorrowLimitCap_ == 0) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidParams);\n }\n\n NATIVE_TOKEN_MAX_BORROW_LIMIT_CAP = nativeTokenMaxBorrowLimitCap_;\n }\n}\n\n/// @notice Fluid Liquidity Governance only related methods\nabstract contract GovernanceModule is IFluidLiquidityAdmin, CommonHelpers, Events, AdminModuleConstants {\n /// @notice only governance guard\n modifier onlyGovernance() {\n if (_getGovernanceAddr() != msg.sender) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__OnlyGovernance);\n }\n _;\n }\n\n /// @dev checks that `value_` is a valid address (not zero address)\n function _checkValidAddress(address value_) internal pure {\n if (value_ == address(0)) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__AddressZero);\n }\n }\n\n /// @dev checks that `value_` address is a contract (which includes address zero check)\n function _checkIsContractOrNativeAddress(address value_) internal view {\n if (value_.code.length == 0 && value_ != NATIVE_TOKEN_ADDRESS) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__AddressNotAContract);\n }\n }\n\n /// @dev checks that `token_` decimals are between `MIN_TOKEN_DECIMALS` and `MAX_TOKEN_DECIMALS` (inclusive).\n function _checkTokenDecimalsRange(address token_) internal view {\n uint8 decimals_ = token_ == NATIVE_TOKEN_ADDRESS ? NATIVE_TOKEN_DECIMALS : IERC20Metadata(token_).decimals();\n if (decimals_ < MIN_TOKEN_DECIMALS || decimals_ > MAX_TOKEN_DECIMALS) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__TokenInvalidDecimalsRange);\n }\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateAuths(AddressBool[] calldata authsStatus_) external onlyGovernance {\n uint256 length_ = authsStatus_.length;\n for (uint256 i; i < length_; ) {\n _checkValidAddress(authsStatus_[i].addr);\n\n _isAuth[authsStatus_[i].addr] = authsStatus_[i].value ? 1 : 0;\n\n unchecked {\n ++i;\n }\n }\n\n emit LogUpdateAuths(authsStatus_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateGuardians(AddressBool[] calldata guardiansStatus_) external onlyGovernance {\n uint256 length_ = guardiansStatus_.length;\n for (uint256 i; i < length_; ) {\n _checkValidAddress(guardiansStatus_[i].addr);\n\n _isGuardian[guardiansStatus_[i].addr] = guardiansStatus_[i].value ? 1 : 0;\n\n unchecked {\n ++i;\n }\n }\n\n emit LogUpdateGuardians(guardiansStatus_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateRevenueCollector(address revenueCollector_) external onlyGovernance {\n _checkValidAddress(revenueCollector_);\n\n _revenueCollector = revenueCollector_;\n\n emit LogUpdateRevenueCollector(revenueCollector_);\n }\n}\n\nabstract contract AuthInternals is Error, CommonHelpers, Events {\n /// @dev computes rata data packed uint256 for version 1 rate input params telling desired values\n /// at different uzilitation points (0%, kink, 100%)\n /// @param rataDataV1Params_ rata data params for a given token\n /// @return rateData_ packed uint256 rate data\n function _computeRateDataPackedV1(\n RateDataV1Params memory rataDataV1Params_\n ) internal pure returns (uint256 rateData_) {\n if (rataDataV1Params_.rateAtUtilizationZero > X16) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__RATE_AT_UTIL_ZERO);\n }\n if (rataDataV1Params_.rateAtUtilizationKink > X16) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__RATE_AT_UTIL_KINK);\n }\n if (rataDataV1Params_.rateAtUtilizationMax > X16) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__RATE_AT_UTIL_MAX);\n }\n if (\n // kink must not be 0 or >= 100% (being 0 or 100% would lead to division through 0 at calculation time)\n rataDataV1Params_.kink == 0 ||\n rataDataV1Params_.kink >= FOUR_DECIMALS ||\n // for the last part of rate curve a spike increase must be present as utilization grows.\n // declining rate is supported before kink. kink to max must be increasing.\n // @dev Note rates can be equal, that leads to a 0 slope which is supported in calculation code.\n rataDataV1Params_.rateAtUtilizationKink > rataDataV1Params_.rateAtUtilizationMax\n ) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidParams);\n }\n\n rateData_ =\n 1 | // version\n (rataDataV1Params_.rateAtUtilizationZero << LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) |\n (rataDataV1Params_.kink << LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) |\n (rataDataV1Params_.rateAtUtilizationKink << LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) |\n (rataDataV1Params_.rateAtUtilizationMax << LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX);\n }\n\n /// @dev computes rata data packed uint256 for rate version 2 input params telling desired values\n /// at different uzilitation points (0%, kink1, kink2, 100%)\n /// @param rataDataV2Params_ rata data params for a given token\n /// @return rateData_ packed uint256 rate data\n function _computeRateDataPackedV2(\n RateDataV2Params memory rataDataV2Params_\n ) internal pure returns (uint256 rateData_) {\n if (rataDataV2Params_.rateAtUtilizationZero > X16) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__RATE_AT_UTIL_ZERO);\n }\n if (rataDataV2Params_.rateAtUtilizationKink1 > X16) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__RATE_AT_UTIL_KINK1);\n }\n if (rataDataV2Params_.rateAtUtilizationKink2 > X16) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__RATE_AT_UTIL_KINK2);\n }\n if (rataDataV2Params_.rateAtUtilizationMax > X16) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__RATE_AT_UTIL_MAX_V2);\n }\n if (\n // kink can not be 0, >= 100% or >= kink2 (would lead to division through 0 at calculation time)\n rataDataV2Params_.kink1 == 0 ||\n rataDataV2Params_.kink1 >= FOUR_DECIMALS ||\n rataDataV2Params_.kink1 >= rataDataV2Params_.kink2 ||\n // kink2 can not be >= 100% (must be > kink1 already checked)\n rataDataV2Params_.kink2 >= FOUR_DECIMALS ||\n // for the last part of rate curve a spike increase must be present as utilization grows.\n // declining rate is supported before kink2. kink2 to max must be increasing.\n // @dev Note rates can be equal, that leads to a 0 slope which is supported in calculation code.\n rataDataV2Params_.rateAtUtilizationKink2 > rataDataV2Params_.rateAtUtilizationMax\n ) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidParams);\n }\n\n rateData_ =\n 2 | // version\n (rataDataV2Params_.rateAtUtilizationZero << LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) |\n (rataDataV2Params_.kink1 << LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) |\n (rataDataV2Params_.rateAtUtilizationKink1 <<\n LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) |\n (rataDataV2Params_.kink2 << LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) |\n (rataDataV2Params_.rateAtUtilizationKink2 <<\n LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) |\n (rataDataV2Params_.rateAtUtilizationMax << LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX);\n }\n\n /// @dev updates the exchange prices in storage for `token_` and returns `supplyExchangePrice_` and `borrowExchangePrice_`.\n /// Recommended to use only in a method that later calls `_updateExchangePricesAndRates()`.\n function _updateExchangePrices(\n address token_\n ) internal returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n uint256 exchangePricesAndConfig_ = _exchangePricesAndConfig[token_];\n\n // calculate the new exchange prices based on earned interest\n (supplyExchangePrice_, borrowExchangePrice_) = LiquidityCalcs.calcExchangePrices(exchangePricesAndConfig_);\n\n // ensure values written to storage do not exceed the dedicated bit space in packed uint256 slots\n if (supplyExchangePrice_ > X64 || borrowExchangePrice_ > X64) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__EXCHANGE_PRICES);\n }\n\n // write updated exchangePrices_ for token to storage\n _exchangePricesAndConfig[token_] =\n (exchangePricesAndConfig_ &\n // mask to update bits: 58-218 (timestamp and exchange prices)\n 0xfffffffff80000000000000000000000000000000000000003ffffffffffffff) |\n (block.timestamp << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) |\n (supplyExchangePrice_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) |\n (borrowExchangePrice_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE);\n\n emit LogUpdateExchangePrices(\n token_,\n supplyExchangePrice_,\n borrowExchangePrice_,\n exchangePricesAndConfig_ & X16, // borrow rate is unchanged -> read from exchangePricesAndConfig_\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14 // utilization is unchanged -> read from exchangePricesAndConfig_\n );\n\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n /// @dev updates the exchange prices + rates in storage for `token_` and returns `supplyExchangePrice_` and `borrowExchangePrice_`\n function _updateExchangePricesAndRates(\n address token_\n ) internal returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n uint256 exchangePricesAndConfig_ = _exchangePricesAndConfig[token_];\n // calculate the new exchange prices based on earned interest\n (supplyExchangePrice_, borrowExchangePrice_) = LiquidityCalcs.calcExchangePrices(exchangePricesAndConfig_);\n\n uint256 totalAmounts_ = _totalAmounts[token_];\n\n // calculate updated ratios\n // set supplyRatio_ = supplyWithInterest here, using that value for total supply before finish calc supplyRatio\n uint256 supplyRatio_ = ((BigMathMinified.fromBigNumber(\n (totalAmounts_ & X64),\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n ) * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n // set borrowRatio_ = borrowWithInterest here, using that value for total borrow before finish calc borrowRatio\n uint256 borrowRatio_ = ((BigMathMinified.fromBigNumber(\n (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n ) * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n\n uint256 supplyInterestFree_ = BigMathMinified.fromBigNumber(\n (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n uint256 borrowInterestFree_ = BigMathMinified.fromBigNumber(\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE),\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n // calculate utilization: totalBorrow / totalSupply. If no supply, utilization must be 0 (avoid division by 0)\n uint256 utilization_ = 0;\n if (supplyRatio_ > 0 || supplyInterestFree_ > 0) {\n utilization_ = (((borrowRatio_ + borrowInterestFree_) * FOUR_DECIMALS) /\n (supplyRatio_ + supplyInterestFree_));\n }\n\n // finish calculating supply & borrow ratio\n // ########## calculating supply ratio ##########\n // supplyRatio_ holds value of supplyWithInterest below\n if (supplyRatio_ > supplyInterestFree_) {\n // supplyRatio_ is ratio with 1 bit as 0 as supply interest raw is bigger\n supplyRatio_ = ((supplyInterestFree_ * FOUR_DECIMALS) / supplyRatio_) << 1;\n // because of checking to divide by bigger amount, ratio can never be > 100%\n } else if (supplyRatio_ < supplyInterestFree_) {\n // supplyRatio_ is ratio with 1 bit as 1 as supply interest free is bigger\n supplyRatio_ = (((supplyRatio_ * FOUR_DECIMALS) / supplyInterestFree_) << 1) | 1;\n // because of checking to divide by bigger amount, ratio can never be > 100%\n } else {\n // supplies match exactly (supplyWithInterest == supplyInterestFree)\n if (supplyRatio_ > 0) {\n // supplies are not 0 -> set ratio to 1 (with first bit set to 0, doesn't matter)\n supplyRatio_ = FOUR_DECIMALS << 1;\n } else {\n // if total supply = 0\n supplyRatio_ = 0;\n }\n }\n\n // ########## calculating borrow ratio ##########\n // borrowRatio_ holds value of borrowWithInterest below\n if (borrowRatio_ > borrowInterestFree_) {\n // borrowRatio_ is ratio with 1 bit as 0 as borrow interest raw is bigger\n borrowRatio_ = ((borrowInterestFree_ * FOUR_DECIMALS) / borrowRatio_) << 1;\n // because of checking to divide by bigger amount, ratio can never be > 100%\n } else if (borrowRatio_ < borrowInterestFree_) {\n // borrowRatio_ is ratio with 1 bit as 1 as borrow interest free is bigger\n borrowRatio_ = (((borrowRatio_ * FOUR_DECIMALS) / borrowInterestFree_) << 1) | 1;\n // because of checking to divide by bigger amount, ratio can never be > 100%\n } else {\n // borrows match exactly (borrowWithInterest == borrowInterestFree)\n if (borrowRatio_ > 0) {\n // borrows are not 0 -> set ratio to 1 (with first bit set to 0, doesn't matter)\n borrowRatio_ = FOUR_DECIMALS << 1;\n } else {\n // if total borrows = 0\n borrowRatio_ = 0;\n }\n }\n\n // updated borrow rate from utilization\n uint256 borrowRate_ = LiquidityCalcs.calcBorrowRateFromUtilization(_rateData[token_], utilization_);\n\n // ensure values written to storage do not exceed the dedicated bit space in packed uint256 slots\n if (supplyExchangePrice_ > X64 || borrowExchangePrice_ > X64) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__EXCHANGE_PRICES);\n }\n if (utilization_ > X14) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__UTILIZATION);\n }\n\n // write updated exchangePrices_ for token to storage\n _exchangePricesAndConfig[token_] =\n (exchangePricesAndConfig_ &\n // mask to update bits: 0-15 (borrow rate), 30-43 (utilization), 58-248 (timestamp, exchange prices, ratios)\n 0xfe000000000000000000000000000000000000000000000003fff0003fff0000) |\n borrowRate_ | // already includes an overflow check in `calcBorrowRateFromUtilization`\n (utilization_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) |\n (block.timestamp << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) |\n (supplyExchangePrice_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) |\n (borrowExchangePrice_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) |\n // ratios can never be > 100%, no overflow check needed\n (supplyRatio_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) |\n (borrowRatio_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO);\n\n emit LogUpdateExchangePrices(token_, supplyExchangePrice_, borrowExchangePrice_, borrowRate_, utilization_);\n\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n}\n\n/// @notice Fluid Liquidity Auths only related methods\nabstract contract AuthModule is AuthInternals, GovernanceModule {\n using BigMathMinified for uint256;\n\n /// @dev max update on storage threshold as a sanity check. threshold is in 1e2, so 500 = 5%.\n /// A higher threshold is not allowed as it would cause the borrow rate to be updated too rarely.\n uint256 private constant MAX_TOKEN_CONFIG_UPDATE_THRESHOLD = 500;\n\n /// @dev only auths guard\n modifier onlyAuths() {\n if (_isAuth[msg.sender] & 1 != 1 && _getGovernanceAddr() != msg.sender) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__OnlyAuths);\n }\n _;\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function collectRevenue(address[] calldata tokens_) external onlyAuths {\n address payable revenueCollector_ = payable(_revenueCollector);\n if (revenueCollector_ == address(0)) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__RevenueCollectorNotSet);\n }\n\n uint256 length_ = tokens_.length;\n for (uint256 i; i < length_; ) {\n _checkIsContractOrNativeAddress(tokens_[i]);\n\n bool isNativeToken_ = tokens_[i] == NATIVE_TOKEN_ADDRESS;\n\n // get revenue amount with updated interest etc.\n uint256 revenueAmount_ = LiquidityCalcs.calcRevenue(\n _totalAmounts[tokens_[i]],\n _exchangePricesAndConfig[tokens_[i]],\n isNativeToken_ ? address(this).balance : IERC20(tokens_[i]).balanceOf(address(this))\n );\n\n if (revenueAmount_ > 0) {\n // transfer token amount to revenueCollector address\n if (isNativeToken_) {\n Address.sendValue(revenueCollector_, revenueAmount_);\n } else {\n SafeERC20.safeTransfer(IERC20(tokens_[i]), revenueCollector_, revenueAmount_);\n }\n }\n\n emit LogCollectRevenue(tokens_[i], revenueAmount_);\n\n unchecked {\n ++i;\n }\n }\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function changeStatus(uint256 newStatus_) external onlyAuths {\n if (newStatus_ == 0 || newStatus_ > 2) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidParams);\n }\n\n _status = newStatus_;\n\n emit LogChangeStatus(newStatus_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateRateDataV1s(RateDataV1Params[] calldata tokensRateData_) external onlyAuths {\n uint256 length_ = tokensRateData_.length;\n uint256 rateData_;\n\n for (uint256 i; i < length_; ) {\n _checkIsContractOrNativeAddress(tokensRateData_[i].token);\n\n // token that is being listed must have between 6 and 18 decimals.\n // setting rate data is the first step for listing a token, so this check blocks any\n // unsupported token to ever be listed at Liquidity\n _checkTokenDecimalsRange(tokensRateData_[i].token);\n\n rateData_ = _rateData[tokensRateData_[i].token];\n\n // apply current rate data to exchange prices before updating to new rate data\n if (rateData_ != 0) {\n _updateExchangePrices(tokensRateData_[i].token);\n }\n\n _rateData[tokensRateData_[i].token] = _computeRateDataPackedV1(tokensRateData_[i]);\n\n if (rateData_ != 0) {\n // apply new rate data to borrow rate\n _updateExchangePricesAndRates(tokensRateData_[i].token);\n }\n\n unchecked {\n ++i;\n }\n }\n\n emit LogUpdateRateDataV1s(tokensRateData_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateRateDataV2s(RateDataV2Params[] calldata tokensRateData_) external onlyAuths {\n uint256 length_ = tokensRateData_.length;\n uint256 rateData_;\n\n for (uint256 i; i < length_; ) {\n _checkIsContractOrNativeAddress(tokensRateData_[i].token);\n\n // token that is being listed must have between 6 and 18 decimals.\n // setting rate data is the first step for listing a token, so this check blocks any\n // unsupported token to ever be listed at Liquidity\n _checkTokenDecimalsRange(tokensRateData_[i].token);\n\n rateData_ = _rateData[tokensRateData_[i].token];\n\n // apply current rate data to exchange prices before updating to new rate data\n if (rateData_ != 0) {\n _updateExchangePrices(tokensRateData_[i].token);\n }\n\n _rateData[tokensRateData_[i].token] = _computeRateDataPackedV2(tokensRateData_[i]);\n\n if (rateData_ != 0) {\n // apply new rate data to borrow rate\n _updateExchangePricesAndRates(tokensRateData_[i].token);\n }\n\n unchecked {\n ++i;\n }\n }\n\n emit LogUpdateRateDataV2s(tokensRateData_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateTokenConfigs(TokenConfig[] calldata tokenConfigs_) external onlyAuths {\n uint256 length_ = tokenConfigs_.length;\n uint256 exchangePricesAndConfig_;\n uint256 supplyExchangePrice_;\n uint256 borrowExchangePrice_;\n\n for (uint256 i; i < length_; ) {\n _checkIsContractOrNativeAddress(tokenConfigs_[i].token);\n if (_rateData[tokenConfigs_[i].token] == 0) {\n // rate data must be configured before token config\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidConfigOrder);\n }\n if (tokenConfigs_[i].fee > FOUR_DECIMALS) {\n // fee can not be > 100%\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__FEE);\n }\n if (tokenConfigs_[i].maxUtilization > FOUR_DECIMALS) {\n // borrows above 100% should never be possible\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__MAX_UTILIZATION);\n }\n if (tokenConfigs_[i].threshold > MAX_TOKEN_CONFIG_UPDATE_THRESHOLD) {\n // update on storage threshold can not be > MAX_TOKEN_CONFIG_UPDATE_THRESHOLD\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__THRESHOLD);\n }\n\n exchangePricesAndConfig_ = _exchangePricesAndConfig[tokenConfigs_[i].token];\n\n // extract exchange prices\n supplyExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &\n X64;\n borrowExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &\n X64;\n\n if (supplyExchangePrice_ > 0 && borrowExchangePrice_ > 0) {\n // calculate the current exchange prices based on earned interest before updating fee + timestamp in storage\n (supplyExchangePrice_, borrowExchangePrice_) = LiquidityCalcs.calcExchangePrices(\n exchangePricesAndConfig_\n );\n\n // ensure values written to storage do not exceed the dedicated bit space in packed uint256 slots\n if (supplyExchangePrice_ > X64 || borrowExchangePrice_ > X64) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__EXCHANGE_PRICES);\n }\n } else {\n // exchange prices can only increase once set so if either one is 0, the other must be 0 too.\n supplyExchangePrice_ = EXCHANGE_PRICES_PRECISION;\n borrowExchangePrice_ = EXCHANGE_PRICES_PRECISION;\n\n _listedTokens.push(tokenConfigs_[i].token);\n }\n\n // max utilization of 100% is default, configs2 slot is not used in that case\n bool usesConfigs2_ = tokenConfigs_[i].maxUtilization != FOUR_DECIMALS;\n\n _exchangePricesAndConfig[tokenConfigs_[i].token] =\n // mask to set bits 16-29 (fee), 44-218 (update storage threshold, timestamp, exchange prices)\n // and flag for uses configs2 at bit 249\n (exchangePricesAndConfig_ & 0xfdfffffff80000000000000000000000000000000000000000000fffc000ffff) |\n (tokenConfigs_[i].fee << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) |\n (tokenConfigs_[i].threshold << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD) |\n (block.timestamp << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) |\n (supplyExchangePrice_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) |\n (borrowExchangePrice_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) |\n ((uint256(usesConfigs2_ ? 1 : 0)) << uint256(LiquiditySlotsLink.BITS_EXCHANGE_PRICES_USES_CONFIGS2));\n\n _configs2[tokenConfigs_[i].token] =\n // set max utilization at bits 0-14\n (_configs2[tokenConfigs_[i].token] &\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc000) |\n (usesConfigs2_ ? tokenConfigs_[i].maxUtilization : 0);\n\n unchecked {\n ++i;\n }\n }\n\n emit LogUpdateTokenConfigs(tokenConfigs_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateUserClasses(AddressUint256[] calldata userClasses_) external onlyAuths {\n uint256 length_ = userClasses_.length;\n for (uint256 i = 0; i < length_; ) {\n if (userClasses_[i].value > 1) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidParams);\n }\n _checkIsContractOrNativeAddress(userClasses_[i].addr);\n\n _userClass[userClasses_[i].addr] = userClasses_[i].value;\n\n unchecked {\n ++i;\n }\n }\n\n emit LogUpdateUserClasses(userClasses_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateUserSupplyConfigs(UserSupplyConfig[] memory userSupplyConfigs_) external onlyAuths {\n uint256 userSupplyData_;\n uint256 totalAmounts_;\n uint256 totalSupplyRawInterest_;\n uint256 totalSupplyInterestFree_;\n uint256 supplyConversion_;\n uint256 withdrawLimitConversion_;\n uint256 supplyExchangePrice_;\n\n for (uint256 i; i < userSupplyConfigs_.length; ) {\n _checkIsContractOrNativeAddress(userSupplyConfigs_[i].user);\n _checkIsContractOrNativeAddress(userSupplyConfigs_[i].token);\n if (_exchangePricesAndConfig[userSupplyConfigs_[i].token] == 0) {\n // token config must be configured before setting any user supply config\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidConfigOrder);\n }\n if (\n userSupplyConfigs_[i].mode > 1 ||\n // can not set expand duration to 0 as that could cause a division by 0 in LiquidityCalcs.\n // having expand duration as 0 is anyway not an expected config so removing the possibility for that.\n // if no expansion is wanted, simply set expandDuration to 1 and expandPercent to 0.\n userSupplyConfigs_[i].expandDuration == 0\n ) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidParams);\n }\n if (userSupplyConfigs_[i].expandPercent > FOUR_DECIMALS) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__EXPAND_PERCENT);\n }\n if (userSupplyConfigs_[i].expandDuration > X24) {\n // duration is max 24 bits\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__EXPAND_DURATION);\n }\n if (userSupplyConfigs_[i].baseWithdrawalLimit == 0) {\n // base withdrawal limit can not be 0. As a side effect, this ensures that there is no supply config\n // where all values would be 0, so configured users can be differentiated in the mapping.\n revert FluidLiquidityError(ErrorTypes.AdminModule__LimitZero);\n }\n // @dev baseWithdrawalLimit has no max bits amount as it is in normal token amount & converted to BigNumber\n\n // get current user config data from storage\n userSupplyData_ = _userSupplyData[userSupplyConfigs_[i].user][userSupplyConfigs_[i].token];\n\n // if userSupplyData_ == 0 (new setup) or if mode is unchanged, normal update is possible.\n // else if mode changes, values have to be converted from raw <> normal etc.\n if (\n userSupplyData_ == 0 ||\n (userSupplyData_ & 1 == 0 && userSupplyConfigs_[i].mode == 0) ||\n (userSupplyData_ & 1 == 1 && userSupplyConfigs_[i].mode == 1)\n ) {\n // Updating user data on storage\n\n _userSupplyData[userSupplyConfigs_[i].user][userSupplyConfigs_[i].token] =\n // mask to update first bit + bits 162-217 (expand percentage, expand duration, base limit)\n (userSupplyData_ & 0xfffffffffc00000000000003fffffffffffffffffffffffffffffffffffffffe) |\n (userSupplyConfigs_[i].mode) | // at first bit\n (userSupplyConfigs_[i].expandPercent << LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) |\n (userSupplyConfigs_[i].expandDuration << LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) |\n // convert base withdrawal limit to BigNumber for storage (10 | 8). (below this, 100% can be withdrawn)\n (userSupplyConfigs_[i].baseWithdrawalLimit.toBigNumber(\n SMALL_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n ) << LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT);\n } else {\n // mode changes -> values have to be converted from raw <> normal etc.\n\n // if the mode changes then update _exchangePricesAndConfig related data in storage always\n // update exchange prices timely before applying changes that affect utilization, rate etc.\n _updateExchangePrices(userSupplyConfigs_[i].token);\n\n // get updated exchange prices for the token\n (supplyExchangePrice_, ) = LiquidityCalcs.calcExchangePrices(\n _exchangePricesAndConfig[userSupplyConfigs_[i].token]\n );\n\n totalAmounts_ = _totalAmounts[userSupplyConfigs_[i].token];\n totalSupplyRawInterest_ = BigMathMinified.fromBigNumber(\n (totalAmounts_ & X64),\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n totalSupplyInterestFree_ = BigMathMinified.fromBigNumber(\n (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n // read current user supply & withdraw limit values\n // here supplyConversion_ = user supply amount\n supplyConversion_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n supplyConversion_ =\n (supplyConversion_ >> DEFAULT_EXPONENT_SIZE) <<\n (supplyConversion_ & DEFAULT_EXPONENT_MASK);\n\n withdrawLimitConversion_ =\n (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) &\n X64; // here withdrawLimitConversion_ = previous user withdraw limit\n withdrawLimitConversion_ =\n (withdrawLimitConversion_ >> DEFAULT_EXPONENT_SIZE) <<\n (withdrawLimitConversion_ & DEFAULT_EXPONENT_MASK);\n\n // conversion of balance and limit according to the mode change\n if (userSupplyData_ & 1 == 0 && userSupplyConfigs_[i].mode == 1) {\n // Changing balance from interest free to with interest -> normal amounts to raw amounts\n // -> must divide by exchange price.\n\n // decreasing interest free total supply\n totalSupplyInterestFree_ = totalSupplyInterestFree_ > supplyConversion_\n ? totalSupplyInterestFree_ - supplyConversion_\n : 0;\n\n supplyConversion_ = (supplyConversion_ * EXCHANGE_PRICES_PRECISION) / supplyExchangePrice_;\n withdrawLimitConversion_ =\n (withdrawLimitConversion_ * EXCHANGE_PRICES_PRECISION) /\n supplyExchangePrice_;\n\n // increasing raw (with interest) total supply\n totalSupplyRawInterest_ += supplyConversion_;\n } else if (userSupplyData_ & 1 == 1 && userSupplyConfigs_[i].mode == 0) {\n // Changing balance from with interest to interest free-> raw amounts to normal amounts\n // -> must multiply by exchange price.\n\n // decreasing raw (with interest) supply\n totalSupplyRawInterest_ = totalSupplyRawInterest_ > supplyConversion_\n ? totalSupplyRawInterest_ - supplyConversion_\n : 0;\n\n supplyConversion_ = (supplyConversion_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION;\n withdrawLimitConversion_ =\n (withdrawLimitConversion_ * supplyExchangePrice_) /\n EXCHANGE_PRICES_PRECISION;\n\n // increasing interest free total supply\n totalSupplyInterestFree_ += supplyConversion_;\n }\n\n // change new converted amounts to BigNumber for storage\n supplyConversion_ = supplyConversion_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n withdrawLimitConversion_ = withdrawLimitConversion_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN // withdrawal limit stores the amount that must stay supplied after withdrawal\n );\n\n // Updating user data on storage\n _userSupplyData[userSupplyConfigs_[i].user][userSupplyConfigs_[i].token] =\n // mask to set bits 0-128 and 162-217 (all except last process timestamp)\n (userSupplyData_ & 0xfffffffffc00000000000003fffffffe00000000000000000000000000000000) |\n (userSupplyConfigs_[i].mode) |\n (supplyConversion_ << LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) | // BigNumber converted can not overflow\n (withdrawLimitConversion_ << LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) | // BigNumber converted can not overflow\n (userSupplyConfigs_[i].expandPercent << LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) |\n (userSupplyConfigs_[i].expandDuration << LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) |\n // convert base withdrawal limit to BigNumber for storage (10 | 8). (below this, 100% can be withdrawn)\n (userSupplyConfigs_[i].baseWithdrawalLimit.toBigNumber(\n SMALL_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n ) << LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT);\n\n // change new total amounts to BigNumber for storage\n totalSupplyRawInterest_ = totalSupplyRawInterest_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n totalSupplyInterestFree_ = totalSupplyInterestFree_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n\n // Updating total supplies on storage\n _totalAmounts[userSupplyConfigs_[i].token] =\n // mask to set bits 0-127\n (totalAmounts_ & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) |\n (totalSupplyRawInterest_) | // BigNumber converted can not overflow\n (totalSupplyInterestFree_ << LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE); // BigNumber converted can not overflow\n\n // trigger update borrow rate, utilization, ratios etc.\n _updateExchangePricesAndRates(userSupplyConfigs_[i].token);\n }\n\n unchecked {\n ++i;\n }\n }\n\n emit LogUpdateUserSupplyConfigs(userSupplyConfigs_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateUserBorrowConfigs(UserBorrowConfig[] memory userBorrowConfigs_) external onlyAuths {\n uint256 userBorrowData_;\n uint256 totalAmounts_;\n uint256 totalBorrowRawInterest_;\n uint256 totalBorrowInterestFree_;\n uint256 borrowingConversion_;\n uint256 debtCeilingConversion_;\n uint256 borrowExchangePrice_;\n\n for (uint256 i; i < userBorrowConfigs_.length; ) {\n _checkIsContractOrNativeAddress(userBorrowConfigs_[i].user);\n _checkIsContractOrNativeAddress(userBorrowConfigs_[i].token);\n if (_exchangePricesAndConfig[userBorrowConfigs_[i].token] == 0) {\n // token config must be configured before setting any user borrow config\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidConfigOrder);\n }\n if (\n userBorrowConfigs_[i].mode > 1 ||\n // max debt ceiling must not be smaller than base debt ceiling. Also covers case where max = 0 but base > 0\n userBorrowConfigs_[i].baseDebtCeiling > userBorrowConfigs_[i].maxDebtCeiling ||\n // can not set expand duration to 0 as that could cause a division by 0 in LiquidityCalcs.\n // having expand duration as 0 is anyway not an expected config so removing the possibility for that.\n // if no expansion is wanted, simply set expandDuration to 1 and expandPercent to 0.\n userBorrowConfigs_[i].expandDuration == 0 ||\n // sanity check that max borrow limit can never be more than 10x the total token supply.\n // protects against that even if someone could artificially inflate token supply to a point where\n // Fluid precision trade-offs could become problematic, can not inflate too much.\n (userBorrowConfigs_[i].maxDebtCeiling >\n (\n userBorrowConfigs_[i].token == NATIVE_TOKEN_ADDRESS\n ? NATIVE_TOKEN_MAX_BORROW_LIMIT_CAP\n : 10 * IERC20(userBorrowConfigs_[i].token).totalSupply()\n ))\n ) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__InvalidParams);\n }\n if (userBorrowConfigs_[i].expandPercent > X14) {\n // expandPercent is max 14 bits\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__EXPAND_PERCENT_BORROW);\n }\n if (userBorrowConfigs_[i].expandDuration > X24) {\n // duration is max 24 bits\n revert FluidLiquidityError(ErrorTypes.AdminModule__ValueOverflow__EXPAND_DURATION_BORROW);\n }\n if (userBorrowConfigs_[i].baseDebtCeiling == 0 || userBorrowConfigs_[i].maxDebtCeiling == 0) {\n // limits can not be 0. As a side effect, this ensures that there is no borrow config\n // where all values would be 0, so configured users can be differentiated in the mapping.\n revert FluidLiquidityError(ErrorTypes.AdminModule__LimitZero);\n }\n // @dev baseDebtCeiling & maxDebtCeiling have no max bits amount as they are in normal token amount\n // and then converted to BigNumber\n\n // get current user config data from storage\n userBorrowData_ = _userBorrowData[userBorrowConfigs_[i].user][userBorrowConfigs_[i].token];\n\n // if userBorrowData_ == 0 (new setup) or if mode is unchanged, normal update is possible.\n // else if mode changes, values have to be converted from raw <> normal etc.\n if (\n userBorrowData_ == 0 ||\n (userBorrowData_ & 1 == 0 && userBorrowConfigs_[i].mode == 0) ||\n (userBorrowData_ & 1 == 1 && userBorrowConfigs_[i].mode == 1)\n ) {\n // Updating user data on storage\n\n _userBorrowData[userBorrowConfigs_[i].user][userBorrowConfigs_[i].token] =\n // mask to update first bit (mode) + bits 162-235 (debt limit values)\n (userBorrowData_ & 0xfffff0000000000000000003fffffffffffffffffffffffffffffffffffffffe) |\n (userBorrowConfigs_[i].mode) |\n (userBorrowConfigs_[i].expandPercent << LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) |\n (userBorrowConfigs_[i].expandDuration << LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) |\n // convert base debt limit to BigNumber for storage (10 | 8). (borrow is always possible below this)\n (userBorrowConfigs_[i].baseDebtCeiling.toBigNumber(\n SMALL_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n ) << LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) |\n // convert max debt limit to BigNumber for storage (10 | 8). (no borrowing ever possible above this)\n (userBorrowConfigs_[i].maxDebtCeiling.toBigNumber(\n SMALL_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n ) << LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT);\n } else {\n // mode changes -> values have to be converted from raw <> normal etc.\n\n // if the mode changes then update _exchangePricesAndConfig related data in storage always\n // update exchange prices timely before applying changes that affect utilization, rate etc.\n _updateExchangePrices(userBorrowConfigs_[i].token);\n\n // get updated exchange prices for the token\n (, borrowExchangePrice_) = LiquidityCalcs.calcExchangePrices(\n _exchangePricesAndConfig[userBorrowConfigs_[i].token]\n );\n\n totalAmounts_ = _totalAmounts[userBorrowConfigs_[i].token];\n totalBorrowRawInterest_ = BigMathMinified.fromBigNumber(\n (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n totalBorrowInterestFree_ = BigMathMinified.fromBigNumber(\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE),\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n // read current user borrowing & borrow limit values\n borrowingConversion_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64; // here borrowingConversion_ = user borrow amount\n borrowingConversion_ =\n (borrowingConversion_ >> DEFAULT_EXPONENT_SIZE) <<\n (borrowingConversion_ & DEFAULT_EXPONENT_MASK);\n\n debtCeilingConversion_ =\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) &\n X64; // here debtCeilingConversion_ = previous user borrow limit\n debtCeilingConversion_ =\n (debtCeilingConversion_ >> DEFAULT_EXPONENT_SIZE) <<\n (debtCeilingConversion_ & DEFAULT_EXPONENT_MASK);\n\n // conversion of balance and limit according to the mode change\n if (userBorrowData_ & 1 == 0 && userBorrowConfigs_[i].mode == 1) {\n // Changing balance from interest free to with interest -> normal amounts to raw amounts\n // -> must divide by exchange price.\n\n // decreasing interest free total borrow; total = total - user borrow\n totalBorrowInterestFree_ = totalBorrowInterestFree_ > borrowingConversion_\n ? totalBorrowInterestFree_ - borrowingConversion_\n : 0;\n\n // round up for user borrow amount\n borrowingConversion_ = FixedPointMathLib.mulDivUp(\n borrowingConversion_,\n EXCHANGE_PRICES_PRECISION,\n borrowExchangePrice_\n );\n debtCeilingConversion_ =\n (debtCeilingConversion_ * EXCHANGE_PRICES_PRECISION) /\n borrowExchangePrice_;\n\n // increasing raw (with interest) total borrow\n totalBorrowRawInterest_ += borrowingConversion_;\n } else if (userBorrowData_ & 1 == 1 && userBorrowConfigs_[i].mode == 0) {\n // Changing balance from with interest to interest free-> raw amounts to normal amounts\n // -> must multiply by exchange price.\n\n // decreasing raw (with interest) borrow; total = total - user borrow raw\n totalBorrowRawInterest_ = totalBorrowRawInterest_ > borrowingConversion_\n ? totalBorrowRawInterest_ - borrowingConversion_\n : 0;\n\n // round up for user borrow amount\n borrowingConversion_ = FixedPointMathLib.mulDivUp(\n borrowingConversion_,\n borrowExchangePrice_,\n EXCHANGE_PRICES_PRECISION\n );\n debtCeilingConversion_ =\n (debtCeilingConversion_ * borrowExchangePrice_) /\n EXCHANGE_PRICES_PRECISION;\n\n // increasing interest free total borrow\n totalBorrowInterestFree_ += borrowingConversion_;\n }\n\n // change new converted amounts to BigNumber for storage\n borrowingConversion_ = borrowingConversion_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_UP\n );\n debtCeilingConversion_ = debtCeilingConversion_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n\n // Updating user data on storage\n _userBorrowData[userBorrowConfigs_[i].user][userBorrowConfigs_[i].token] =\n // mask to update bits 0-128 and bits 162-235 (all except last process timestamp)\n (userBorrowData_ & 0xfffff0000000000000000003fffffffe00000000000000000000000000000000) |\n (userBorrowConfigs_[i].mode) |\n (borrowingConversion_ << LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) | // BigNumber converted can not overflow\n (debtCeilingConversion_ << LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) | // BigNumber converted can not overflow\n (userBorrowConfigs_[i].expandPercent << LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) |\n (userBorrowConfigs_[i].expandDuration << LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) |\n // convert base debt limit to BigNumber for storage (10 | 8). (borrow is always possible below this)\n (userBorrowConfigs_[i].baseDebtCeiling.toBigNumber(\n SMALL_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n ) << LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) |\n // convert max debt limit to BigNumber for storage (10 | 8). (no borrowing ever possible above this)\n (userBorrowConfigs_[i].maxDebtCeiling.toBigNumber(\n SMALL_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n ) << LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT);\n\n // change new total amounts to BigNumber for storage\n totalBorrowRawInterest_ = totalBorrowRawInterest_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_UP\n );\n totalBorrowInterestFree_ = totalBorrowInterestFree_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_UP\n );\n\n // Updating total borrowings on storage\n _totalAmounts[userBorrowConfigs_[i].token] =\n // mask to set bits 128-255\n (totalAmounts_ & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff) |\n (totalBorrowRawInterest_ << LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) | // BigNumber converted can not overflow\n (totalBorrowInterestFree_ << LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE); // BigNumber converted can not overflow\n\n // trigger update borrow rate, utilization, ratios etc.\n _updateExchangePricesAndRates(userBorrowConfigs_[i].token);\n }\n\n unchecked {\n ++i;\n }\n }\n\n emit LogUpdateUserBorrowConfigs(userBorrowConfigs_);\n }\n}\n\n/// @notice Fluid Liquidity Guardians only related methods\nabstract contract GuardianModule is AuthModule {\n /// @dev only guardians guard\n modifier onlyGuardians() {\n if (_isGuardian[msg.sender] & 1 != 1 && _getGovernanceAddr() != msg.sender) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__OnlyGuardians);\n }\n _;\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function pauseUser(\n address user_,\n address[] calldata supplyTokens_,\n address[] calldata borrowTokens_\n ) public onlyGuardians {\n _checkIsContractOrNativeAddress(user_);\n if (_userClass[user_] == 1) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__UserNotPausable);\n }\n\n uint256 userData_;\n\n // pause supply tokens\n uint256 length_ = supplyTokens_.length;\n\n if (length_ > 0) {\n for (uint256 i; i < length_; ) {\n _checkIsContractOrNativeAddress(supplyTokens_[i]);\n // userData_ => userSupplyData_\n userData_ = _userSupplyData[user_][supplyTokens_[i]];\n if (userData_ == 0) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__UserNotDefined);\n }\n // set last bit of _userSupplyData (pause flag) to 1\n _userSupplyData[user_][supplyTokens_[i]] =\n userData_ |\n (1 << LiquiditySlotsLink.BITS_USER_SUPPLY_IS_PAUSED);\n\n unchecked {\n ++i;\n }\n }\n }\n\n // pause borrow tokens\n length_ = borrowTokens_.length;\n\n if (length_ > 0) {\n for (uint256 i; i < length_; ) {\n _checkIsContractOrNativeAddress(borrowTokens_[i]);\n // userData_ => userBorrowData_\n userData_ = _userBorrowData[user_][borrowTokens_[i]];\n if (userData_ == 0) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__UserNotDefined);\n }\n // set last bit of _userBorrowData (pause flag) to 1\n _userBorrowData[user_][borrowTokens_[i]] =\n userData_ |\n (1 << LiquiditySlotsLink.BITS_USER_BORROW_IS_PAUSED);\n\n unchecked {\n ++i;\n }\n }\n }\n\n emit LogPauseUser(user_, supplyTokens_, borrowTokens_);\n }\n\n /// @inheritdoc IFluidLiquidityAdmin\n function unpauseUser(\n address user_,\n address[] calldata supplyTokens_,\n address[] calldata borrowTokens_\n ) public onlyGuardians {\n _checkIsContractOrNativeAddress(user_);\n\n uint256 userData_;\n\n // unpause supply tokens\n uint256 length_ = supplyTokens_.length;\n\n if (length_ > 0) {\n for (uint256 i; i < length_; ) {\n _checkIsContractOrNativeAddress(supplyTokens_[i]);\n // userData_ => userSupplyData_\n userData_ = _userSupplyData[user_][supplyTokens_[i]];\n if ((userData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_IS_PAUSED) & 1 != 1) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__UserNotPaused);\n }\n\n // set last bit of _userSupplyData (pause flag) to 0\n _userSupplyData[user_][supplyTokens_[i]] =\n userData_ &\n 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n\n unchecked {\n ++i;\n }\n }\n }\n\n // unpause borrow tokens\n length_ = borrowTokens_.length;\n\n if (length_ > 0) {\n for (uint256 i; i < length_; ) {\n _checkIsContractOrNativeAddress(borrowTokens_[i]);\n // userData_ => userBorrowData_\n userData_ = _userBorrowData[user_][borrowTokens_[i]];\n if ((userData_ >> LiquiditySlotsLink.BITS_USER_BORROW_IS_PAUSED) & 1 != 1) {\n revert FluidLiquidityError(ErrorTypes.AdminModule__UserNotPaused);\n }\n // set last bit of _userBorrowData (pause flag) to 0\n _userBorrowData[user_][borrowTokens_[i]] =\n userData_ &\n 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n\n unchecked {\n ++i;\n }\n }\n }\n\n emit LogUnpauseUser(user_, supplyTokens_, borrowTokens_);\n }\n}\n\n/// @title Fluid Liquidity AdminModule\n/// @notice Fluid Liquidity auth protected methods to configure things such as:\n/// guardians, auths, governance, revenue, token configs, allowances etc.\n/// Accessibility of methods is restricted to Governance, Auths or Guardians. Governance is Auth & Governance by default\ncontract FluidLiquidityAdminModule is AdminModuleConstants, GuardianModule {\n constructor(uint256 nativeTokenMaxBorrowLimitCap_) AdminModuleConstants(nativeTokenMaxBorrowLimitCap_) {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateExchangePrices(\n address[] calldata tokens_\n ) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_) {\n uint256 tokensLength_ = tokens_.length;\n\n supplyExchangePrices_ = new uint256[](tokensLength_);\n borrowExchangePrices_ = new uint256[](tokensLength_);\n\n for (uint256 i; i < tokensLength_; ) {\n _checkIsContractOrNativeAddress(tokens_[i]);\n (supplyExchangePrices_[i], borrowExchangePrices_[i]) = _updateExchangePricesAndRates(tokens_[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n}\n" }, "contracts/liquidity/adminModule/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Structs {\n struct AddressBool {\n address addr;\n bool value;\n }\n\n struct AddressUint256 {\n address addr;\n uint256 value;\n }\n\n /// @notice struct to set borrow rate data for version 1\n struct RateDataV1Params {\n ///\n /// @param token for rate data\n address token;\n ///\n /// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast\n uint256 kink;\n ///\n /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100\n /// i.e. constant minimum borrow rate\n /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)\n uint256 rateAtUtilizationZero;\n ///\n /// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700\n uint256 rateAtUtilizationKink;\n ///\n /// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500\n uint256 rateAtUtilizationMax;\n }\n\n /// @notice struct to set borrow rate data for version 2\n struct RateDataV2Params {\n ///\n /// @param token for rate data\n address token;\n ///\n /// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster\n uint256 kink1;\n ///\n /// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast\n uint256 kink2;\n ///\n /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100\n /// i.e. constant minimum borrow rate\n /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)\n uint256 rateAtUtilizationZero;\n ///\n /// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700\n uint256 rateAtUtilizationKink1;\n ///\n /// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200\n uint256 rateAtUtilizationKink2;\n ///\n /// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500\n uint256 rateAtUtilizationMax;\n }\n\n /// @notice struct to set token config\n struct TokenConfig {\n ///\n /// @param token address\n address token;\n ///\n /// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100\n uint256 fee;\n ///\n /// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100\n uint256 threshold;\n ///\n /// @param maxUtilization maximum allowed utilization. in 1e2: 100% = 10_000; 1% = 100\n /// set to 100% to disable and have default limit of 100% (avoiding SLOAD).\n uint256 maxUtilization;\n }\n\n /// @notice struct to set user supply & withdrawal config\n struct UserSupplyConfig {\n ///\n /// @param user address\n address user;\n ///\n /// @param token address\n address token;\n ///\n /// @param mode: 0 = without interest. 1 = with interest\n uint8 mode;\n ///\n /// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100\n /// Also used to calculate rate at which withdrawal limit should decrease (instant).\n uint256 expandPercent;\n ///\n /// @param expandDuration withdrawal limit expand duration in seconds.\n /// used to calculate rate together with expandPercent\n uint256 expandDuration;\n ///\n /// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 baseWithdrawalLimit;\n }\n\n /// @notice struct to set user borrow & payback config\n struct UserBorrowConfig {\n ///\n /// @param user address\n address user;\n ///\n /// @param token address\n address token;\n ///\n /// @param mode: 0 = without interest. 1 = with interest\n uint8 mode;\n ///\n /// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100\n /// Also used to calculate rate at which debt limit should decrease (instant).\n uint256 expandPercent;\n ///\n /// @param expandDuration debt limit expand duration in seconds.\n /// used to calculate rate together with expandPercent\n uint256 expandDuration;\n ///\n /// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling\n /// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 baseDebtCeiling;\n ///\n /// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 maxDebtCeiling;\n }\n}\n" }, "contracts/liquidity/common/helpers.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"./variables.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\n/// @dev ReentrancyGuard based on OpenZeppelin implementation.\n/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/security/ReentrancyGuard.sol\nabstract contract ReentrancyGuard is Variables, Error {\n uint8 internal constant REENTRANCY_NOT_ENTERED = 1;\n uint8 internal constant REENTRANCY_ENTERED = 2;\n\n constructor() {\n // on logic contracts, switch reentrancy to entered so no call is possible (forces delegatecall)\n _status = REENTRANCY_ENTERED; \n }\n\n /// @dev Prevents a contract from calling itself, directly or indirectly.\n /// See OpenZeppelin implementation for more info\n modifier reentrancy() {\n // On the first call to nonReentrant, _status will be NOT_ENTERED\n if (_status == REENTRANCY_ENTERED) {\n revert FluidLiquidityError(ErrorTypes.LiquidityHelpers__Reentrancy);\n }\n\n // Any calls to nonReentrant after this point will fail\n _status = REENTRANCY_ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = REENTRANCY_NOT_ENTERED;\n }\n}\n\nabstract contract CommonHelpers is ReentrancyGuard {\n /// @dev Returns the current admin (governance).\n function _getGovernanceAddr() internal view returns (address governance_) {\n assembly {\n governance_ := sload(GOVERNANCE_SLOT)\n }\n }\n}\n" }, "contracts/liquidity/common/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract ConstantVariables {\n /// @dev Storage slot with the admin of the contract. Logic from \"proxy.sol\".\n /// This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is validated in the constructor.\n bytes32 internal constant GOVERNANCE_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev address that is mapped to the chain native token\n address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n /// @dev limit for triggering a revert if sent along excess `msg.value` is bigger than this amount\n uint256 internal constant NATIVE_AMOUNT_EXCESS_LIMIT = 1e9;\n /// @dev decimals for native token\n // !! Double check compatibility with all code if this ever changes for a deployment !!\n uint8 internal constant NATIVE_TOKEN_DECIMALS = 18;\n\n /// @dev Minimum token decimals for any token that can be listed at Liquidity (inclusive)\n uint8 internal constant MIN_TOKEN_DECIMALS = 6;\n /// @dev Maximum token decimals for any token that can be listed at Liquidity (inclusive)\n uint8 internal constant MAX_TOKEN_DECIMALS = 18;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n\n /// @dev limit any total amount to be half of type(uint128).max (~3.4e38) at type(int128).max (~1.7e38) as safety\n /// measure for any potential overflows / unexpected outcomes. This is checked for total borrow / supply.\n uint256 internal constant MAX_TOKEN_AMOUNT_CAP = uint256(uint128(type(int128).max));\n\n /// @dev time after which a write to storage of exchangePricesAndConfig will happen always.\n uint256 internal constant FORCE_STORAGE_WRITE_AFTER_TIME = 1 days;\n\n /// @dev constants used for BigMath conversion from and to storage\n uint256 internal constant SMALL_COEFFICIENT_SIZE = 10;\n uint256 internal constant DEFAULT_COEFFICIENT_SIZE = 56;\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;\n\n /// @dev constants to increase readability for using bit masks\n uint256 internal constant FOUR_DECIMALS = 1e4;\n uint256 internal constant TWELVE_DECIMALS = 1e12;\n uint256 internal constant X8 = 0xff;\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X15 = 0x7fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n uint256 internal constant X33 = 0x1ffffffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n}\n\ncontract Variables is ConstantVariables {\n /// @dev address of contract that gets sent the revenue. Configurable by governance\n address internal _revenueCollector;\n\n // 12 bytes empty\n\n // ----- storage slot 1 ------\n\n /// @dev paused status: status = 1 -> normal. status = 2 -> paused.\n /// not tightly packed with revenueCollector address to allow for potential changes later that improve gas more\n /// (revenueCollector is only rarely used by admin methods, where optimization is not as important).\n /// to be replaced with transient storage once EIP-1153 Transient storage becomes available with dencun upgrade.\n uint256 internal _status;\n\n // ----- storage slot 2 ------\n\n /// @dev Auths can set most config values. E.g. contracts that automate certain flows like e.g. adding a new fToken.\n /// Governance can add/remove auths.\n /// Governance is auth by default\n mapping(address => uint256) internal _isAuth;\n\n // ----- storage slot 3 ------\n\n /// @dev Guardians can pause lower class users\n /// Governance can add/remove guardians\n /// Governance is guardian by default\n mapping(address => uint256) internal _isGuardian;\n\n // ----- storage slot 4 ------\n\n /// @dev class defines which protocols can be paused by guardians\n /// Currently there are 2 classes: 0 can be paused by guardians. 1 cannot be paused by guardians.\n /// New protocols are added as class 0 and will be upgraded to 1 over time.\n mapping(address => uint256) internal _userClass;\n\n // ----- storage slot 5 ------\n\n /// @dev exchange prices and token config per token: token -> exchange prices & config\n /// First 16 bits => 0- 15 => borrow rate (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 14 bits => 16- 29 => fee on interest from borrowers to lenders (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383). configurable.\n /// Next 14 bits => 30- 43 => last stored utilization (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n /// Next 14 bits => 44- 57 => update on storage threshold (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383). configurable.\n /// Next 33 bits => 58- 90 => last update timestamp (enough until 16 March 2242 -> max value 8589934591)\n /// Next 64 bits => 91-154 => supply exchange price (1e12 -> max value 18_446_744,073709551615)\n /// Next 64 bits => 155-218 => borrow exchange price (1e12 -> max value 18_446_744,073709551615)\n /// Next 1 bit => 219-219 => if 0 then ratio is supplyInterestFree / supplyWithInterest else ratio is supplyWithInterest / supplyInterestFree\n /// Next 14 bits => 220-233 => supplyRatio: supplyInterestFree / supplyWithInterest (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n /// Next 1 bit => 234-234 => if 0 then ratio is borrowInterestFree / borrowWithInterest else ratio is borrowWithInterest / borrowInterestFree\n /// Next 14 bits => 235-248 => borrowRatio: borrowInterestFree / borrowWithInterest (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n /// Next 1 bit => 249-249 => flag for token uses config storage slot 2. (signals SLOAD for additional config slot is needed during execution)\n /// Last 6 bits => 250-255 => empty for future use\n /// if more free bits are needed in the future, update on storage threshold bits could be reduced to 7 bits\n /// (can plan to add `MAX_TOKEN_CONFIG_UPDATE_THRESHOLD` but need to adjust more bits)\n /// if more bits absolutely needed then we can convert fee, utilization, update on storage threshold,\n /// supplyRatio & borrowRatio from 14 bits to 10bits (1023 max number) where 1000 = 100% & 1 = 0.1%\n mapping(address => uint256) internal _exchangePricesAndConfig;\n\n // ----- storage slot 6 ------\n\n /// @dev Rate related data per token: token -> rate data\n /// READ (SLOAD): all actions; WRITE (SSTORE): only on set config admin actions\n /// token => rate related data\n /// First 4 bits => 0-3 => rate version\n /// rest of the bits are rate dependent:\n\n /// For rate v1 (one kink) ------------------------------------------------------\n /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 52- 67 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Last 188 bits => 68-255 => empty for future use\n\n /// For rate v2 (two kinks) -----------------------------------------------------\n /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 52- 67 => Utilization at kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 68- 83 => Rate at utilization kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 84- 99 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Last 156 bits => 100-255 => empty for future use\n mapping(address => uint256) internal _rateData;\n\n // ----- storage slot 7 ------\n\n /// @dev total supply / borrow amounts for with / without interest per token: token -> amounts\n /// First 64 bits => 0- 63 => total supply with interest in raw (totalSupply = totalSupplyRaw * supplyExchangePrice); BigMath: 56 | 8\n /// Next 64 bits => 64-127 => total interest free supply in normal token amount (totalSupply = totalSupply); BigMath: 56 | 8\n /// Next 64 bits => 128-191 => total borrow with interest in raw (totalBorrow = totalBorrowRaw * borrowExchangePrice); BigMath: 56 | 8\n /// Next 64 bits => 192-255 => total interest free borrow in normal token amount (totalBorrow = totalBorrow); BigMath: 56 | 8\n mapping(address => uint256) internal _totalAmounts;\n\n // ----- storage slot 8 ------\n\n /// @dev user supply data per token: user -> token -> data\n /// First 1 bit => 0 => mode: user supply with or without interest\n /// 0 = without, amounts are in normal (i.e. no need to multiply with exchange price)\n /// 1 = with interest, amounts are in raw (i.e. must multiply with exchange price to get actual token amounts)\n /// Next 64 bits => 1- 64 => user supply amount (normal or raw depends on 1st bit); BigMath: 56 | 8\n /// Next 64 bits => 65-128 => previous user withdrawal limit (normal or raw depends on 1st bit); BigMath: 56 | 8\n /// Next 33 bits => 129-161 => last triggered process timestamp (enough until 16 March 2242 -> max value 8589934591)\n /// Next 14 bits => 162-175 => expand withdrawal limit percentage (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383).\n /// @dev shrinking is instant\n /// Next 24 bits => 176-199 => withdrawal limit expand duration in seconds.(Max value 16_777_215; ~4_660 hours, ~194 days)\n /// Next 18 bits => 200-217 => base withdrawal limit: below this, 100% withdrawals can be done (normal or raw depends on 1st bit); BigMath: 10 | 8\n /// Next 37 bits => 218-254 => empty for future use\n /// Last bit => 255-255 => is user paused (1 = paused, 0 = not paused)\n mapping(address => mapping(address => uint256)) internal _userSupplyData;\n\n // ----- storage slot 9 ------\n\n /// @dev user borrow data per token: user -> token -> data\n /// First 1 bit => 0 => mode: user borrow with or without interest\n /// 0 = without, amounts are in normal (i.e. no need to multiply with exchange price)\n /// 1 = with interest, amounts are in raw (i.e. must multiply with exchange price to get actual token amounts)\n /// Next 64 bits => 1- 64 => user borrow amount (normal or raw depends on 1st bit); BigMath: 56 | 8\n /// Next 64 bits => 65-128 => previous user debt ceiling (normal or raw depends on 1st bit); BigMath: 56 | 8\n /// Next 33 bits => 129-161 => last triggered process timestamp (enough until 16 March 2242 -> max value 8589934591)\n /// Next 14 bits => 162-175 => expand debt ceiling percentage (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n /// @dev shrinking is instant\n /// Next 24 bits => 176-199 => debt ceiling expand duration in seconds (Max value 16_777_215; ~4_660 hours, ~194 days)\n /// Next 18 bits => 200-217 => base debt ceiling: below this, there's no debt ceiling limits (normal or raw depends on 1st bit); BigMath: 10 | 8\n /// Next 18 bits => 218-235 => max debt ceiling: absolute maximum debt ceiling can expand to (normal or raw depends on 1st bit); BigMath: 10 | 8\n /// Next 19 bits => 236-254 => empty for future use\n /// Last bit => 255-255 => is user paused (1 = paused, 0 = not paused)\n mapping(address => mapping(address => uint256)) internal _userBorrowData;\n\n // ----- storage slot 10 ------\n\n /// @dev list of allowed tokens at Liquidity. tokens that are once configured can never be completely removed. so this\n /// array is append-only.\n address[] internal _listedTokens;\n\n // ----- storage slot 11 ------\n\n /// @dev expanded token configs per token: token -> config data slot 2.\n /// Use of this is signaled by `_exchangePricesAndConfig` bit 249.\n /// First 14 bits => 0- 13 => max allowed utilization (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383). configurable.\n /// Last 242 bits => 14-255 => empty for future use\n mapping(address => uint256) internal _configs2;\n}\n" }, "contracts/liquidity/dummyImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidityLogic, IFluidLiquidityAdmin } from \"./interfaces/iLiquidity.sol\";\nimport { Structs as AdminModuleStructs } from \"./adminModule/structs.sol\";\n\n/// @notice Liquidity dummy implementation used for Fluid Liquidity infinite proxy.\n/// @dev see https://github.com/Instadapp/infinite-proxy?tab=readme-ov-file#dummy-implementation\ncontract FluidLiquidityDummyImpl is IFluidLiquidityLogic {\n /// @inheritdoc IFluidLiquidityAdmin\n function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateRevenueCollector(address revenueCollector_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function changeStatus(uint256 newStatus_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function collectRevenue(address[] calldata tokens_) external {}\n\n /// @inheritdoc IFluidLiquidityAdmin\n function updateExchangePrices(\n address[] calldata tokens_\n ) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_) {}\n\n /// @inheritdoc IFluidLiquidityLogic\n function operate(\n address token_,\n int256 supplyAmount_,\n int256 borrowAmount_,\n address withdrawTo_,\n address borrowTo_,\n bytes calldata callbackData_\n ) external payable returns (uint256 memVar3_, uint256 memVar4_) {}\n}\n" }, "contracts/liquidity/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Error {\n error FluidLiquidityError(uint256 errorId_);\n}\n" }, "contracts/liquidity/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | Admin Module | \n |__________________________________*/\n\n /// @notice thrown when an input address is zero\n uint256 internal constant AdminModule__AddressZero = 10001;\n\n /// @notice thrown when msg.sender is not governance\n uint256 internal constant AdminModule__OnlyGovernance = 10002;\n\n /// @notice thrown when msg.sender is not auth\n uint256 internal constant AdminModule__OnlyAuths = 10003;\n\n /// @notice thrown when msg.sender is not guardian\n uint256 internal constant AdminModule__OnlyGuardians = 10004;\n\n /// @notice thrown when base withdrawal limit, base debt limit or max withdrawal limit is sent as 0\n uint256 internal constant AdminModule__LimitZero = 10005;\n\n /// @notice thrown whenever an invalid input param is given\n uint256 internal constant AdminModule__InvalidParams = 10006;\n\n /// @notice thrown if user class 1 is paused (can not be paused)\n uint256 internal constant AdminModule__UserNotPausable = 10007;\n\n /// @notice thrown if user is tried to be unpaused but is not paused in the first place\n uint256 internal constant AdminModule__UserNotPaused = 10008;\n\n /// @notice thrown if user is not defined yet: Governance didn't yet set any config for this user on a particular token\n uint256 internal constant AdminModule__UserNotDefined = 10009;\n\n /// @notice thrown if a token is configured in an invalid order: 1. Set rate config for token 2. Set token config 3. allow any user.\n uint256 internal constant AdminModule__InvalidConfigOrder = 10010;\n\n /// @notice thrown if revenue is collected when revenue collector address is not set\n uint256 internal constant AdminModule__RevenueCollectorNotSet = 10011;\n\n /// @notice all ValueOverflow errors below are thrown if a certain input param overflows the allowed storage size\n uint256 internal constant AdminModule__ValueOverflow__RATE_AT_UTIL_ZERO = 10012;\n uint256 internal constant AdminModule__ValueOverflow__RATE_AT_UTIL_KINK = 10013;\n uint256 internal constant AdminModule__ValueOverflow__RATE_AT_UTIL_MAX = 10014;\n uint256 internal constant AdminModule__ValueOverflow__RATE_AT_UTIL_KINK1 = 10015;\n uint256 internal constant AdminModule__ValueOverflow__RATE_AT_UTIL_KINK2 = 10016;\n uint256 internal constant AdminModule__ValueOverflow__RATE_AT_UTIL_MAX_V2 = 10017;\n uint256 internal constant AdminModule__ValueOverflow__FEE = 10018;\n uint256 internal constant AdminModule__ValueOverflow__THRESHOLD = 10019;\n uint256 internal constant AdminModule__ValueOverflow__EXPAND_PERCENT = 10020;\n uint256 internal constant AdminModule__ValueOverflow__EXPAND_DURATION = 10021;\n uint256 internal constant AdminModule__ValueOverflow__EXPAND_PERCENT_BORROW = 10022;\n uint256 internal constant AdminModule__ValueOverflow__EXPAND_DURATION_BORROW = 10023;\n uint256 internal constant AdminModule__ValueOverflow__EXCHANGE_PRICES = 10024;\n uint256 internal constant AdminModule__ValueOverflow__UTILIZATION = 10025;\n\n /// @notice thrown when an address is not a contract\n uint256 internal constant AdminModule__AddressNotAContract = 10026;\n\n uint256 internal constant AdminModule__ValueOverflow__MAX_UTILIZATION = 10027;\n\n /// @notice thrown if a token that is being listed has not between 6 and 18 decimals\n uint256 internal constant AdminModule__TokenInvalidDecimalsRange = 10028;\n\n /***********************************|\n | User Module | \n |__________________________________*/\n\n /// @notice thrown when user operations are paused for an interacted token\n uint256 internal constant UserModule__UserNotDefined = 11001;\n\n /// @notice thrown when user operations are paused for an interacted token\n uint256 internal constant UserModule__UserPaused = 11002;\n\n /// @notice thrown when user's try to withdraw below withdrawal limit\n uint256 internal constant UserModule__WithdrawalLimitReached = 11003;\n\n /// @notice thrown when user's try to borrow above borrow limit\n uint256 internal constant UserModule__BorrowLimitReached = 11004;\n\n /// @notice thrown when user sent supply/withdraw and borrow/payback both as 0\n uint256 internal constant UserModule__OperateAmountsZero = 11005;\n\n /// @notice thrown when user sent supply/withdraw or borrow/payback both as bigger than 2**128\n uint256 internal constant UserModule__OperateAmountOutOfBounds = 11006;\n\n /// @notice thrown when the operate amount for supply / withdraw / borrow / payback is below the minimum amount\n /// that would cause a storage difference after BigMath & rounding imprecision. Extremely unlikely to ever happen\n /// for all normal use-cases.\n uint256 internal constant UserModule__OperateAmountInsufficient = 11007;\n\n /// @notice thrown when withdraw or borrow is executed but withdrawTo or borrowTo is the zero address\n uint256 internal constant UserModule__ReceiverNotDefined = 11008;\n\n /// @notice thrown when user did send excess or insufficient amount (beyond rounding issues)\n uint256 internal constant UserModule__TransferAmountOutOfBounds = 11009;\n\n /// @notice thrown when user sent msg.value along for an operation not for the native token\n uint256 internal constant UserModule__MsgValueForNonNativeToken = 11010;\n\n /// @notice thrown when a borrow operation is done when utilization is above 100%\n uint256 internal constant UserModule__MaxUtilizationReached = 11011;\n\n /// @notice all ValueOverflow errors below are thrown if a certain input param or calc result overflows the allowed storage size\n uint256 internal constant UserModule__ValueOverflow__EXCHANGE_PRICES = 11012;\n uint256 internal constant UserModule__ValueOverflow__UTILIZATION = 11013;\n uint256 internal constant UserModule__ValueOverflow__TOTAL_SUPPLY = 11014;\n uint256 internal constant UserModule__ValueOverflow__TOTAL_BORROW = 11015;\n\n /***********************************|\n | LiquidityHelpers | \n |__________________________________*/\n\n /// @notice thrown when a reentrancy happens\n uint256 internal constant LiquidityHelpers__Reentrancy = 12001;\n}\n" }, "contracts/liquidity/interfaces/iLiquidity.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IProxy } from \"../../infiniteProxy/interfaces/iProxy.sol\";\nimport { Structs as AdminModuleStructs } from \"../adminModule/structs.sol\";\n\ninterface IFluidLiquidityAdmin {\n /// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract.\n /// auths can be helpful in reducing governance overhead where it's not needed.\n /// @param authsStatus_ array of structs setting allowed status for an address.\n /// status true => add auth, false => remove auth\n function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external;\n\n /// @notice adds/removes guardians. Only callable by Governance.\n /// @param guardiansStatus_ array of structs setting allowed status for an address.\n /// status true => add guardian, false => remove guardian\n function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external;\n\n /// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance.\n /// @param revenueCollector_ new revenue collector address\n function updateRevenueCollector(address revenueCollector_) external;\n\n /// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths.\n /// @param newStatus_ new status\n /// status = 2 -> pause, status = 1 -> resume.\n function changeStatus(uint256 newStatus_) external;\n\n /// @notice update tokens rate data version 1. Only callable by Auths.\n /// @param tokensRateData_ array of RateDataV1Params with rate data to set for each token\n function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external;\n\n /// @notice update tokens rate data version 2. Only callable by Auths.\n /// @param tokensRateData_ array of RateDataV2Params with rate data to set for each token\n function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external;\n\n /// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold.\n /// Only callable by Auths.\n /// @param tokenConfigs_ contains token address, fee & utilization threshold\n function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external;\n\n /// @notice updates user classes: 0 is for new protocols, 1 is for established protocols.\n /// Only callable by Auths.\n /// @param userClasses_ struct array of uint256 value to assign for each user address\n function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external;\n\n /// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits.\n /// Only callable by Auths.\n /// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info\n function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external;\n\n /// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits.\n /// Only callable by Auths.\n /// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info\n function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external;\n\n /// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians).\n /// Only callable by Guardians.\n /// @param user_ address of user to pause operations for\n /// @param supplyTokens_ token addresses to pause withdrawals for\n /// @param borrowTokens_ token addresses to pause borrowings for\n function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;\n\n /// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians).\n /// Only callable by Guardians.\n /// @param user_ address of user to unpause operations for\n /// @param supplyTokens_ token addresses to unpause withdrawals for\n /// @param borrowTokens_ token addresses to unpause borrowings for\n function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;\n\n /// @notice collects revenue for tokens to configured revenueCollector address.\n /// @param tokens_ array of tokens to collect revenue for\n /// @dev Note that this can revert if token balance is < revenueAmount (utilization > 100%)\n function collectRevenue(address[] calldata tokens_) external;\n\n /// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage.\n /// @param tokens_ tokens to update exchange prices for\n /// @return supplyExchangePrices_ new supply rates of overall system for each token\n /// @return borrowExchangePrices_ new borrow rates of overall system for each token\n function updateExchangePrices(\n address[] calldata tokens_\n ) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_);\n}\n\ninterface IFluidLiquidityLogic is IFluidLiquidityAdmin {\n /// @notice Single function which handles supply, withdraw, borrow & payback\n /// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native)\n /// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing\n /// @param borrowAmount_ if +ve then borrow, if -ve then payback, if 0 then nothing\n /// @param withdrawTo_ if withdrawal then to which address\n /// @param borrowTo_ if borrow then to which address\n /// @param callbackData_ callback data passed to `liquidityCallback` method of protocol\n /// @return memVar3_ updated supplyExchangePrice\n /// @return memVar4_ updated borrowExchangePrice\n /// @dev to trigger skipping in / out transfers when in&out amounts balance themselves out (gas optimization):\n /// - supply(+) == borrow(+), withdraw(-) == payback(-).\n /// - `withdrawTo_` / `borrowTo_` must be msg.sender (protocol)\n /// - `callbackData_` MUST be encoded so that \"from\" address is at last 20 bytes (if this optimization is desired),\n /// also for native token operations where liquidityCallback is not triggered!\n /// from address must come at last position if there is more data. I.e. encode like:\n /// abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end\n /// so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end.\n function operate(\n address token_,\n int256 supplyAmount_,\n int256 borrowAmount_,\n address withdrawTo_,\n address borrowTo_,\n bytes calldata callbackData_\n ) external payable returns (uint256 memVar3_, uint256 memVar4_);\n}\n\ninterface IFluidLiquidity is IProxy, IFluidLiquidityLogic {}\n" }, "contracts/liquidity/proxy.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Proxy } from \"../infiniteProxy/proxy.sol\";\n\n/// @notice Fluid Liquidity infinte proxy.\n/// Liquidity is the central point of the Instadapp Fluid architecture, it is the core interaction point\n/// for all allow-listed protocols, such as fTokens, Vault, Flashloan, StETH protocol, DEX protocol etc.\ncontract FluidLiquidityProxy is Proxy {\n constructor(address admin_, address dummyImplementation_) Proxy(admin_, dummyImplementation_) {}\n}\n" }, "contracts/liquidity/userModule/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Events {\n /// @notice emitted on any `operate()` execution: deposit / supply / withdraw / borrow.\n /// includes info related to the executed operation, new total amounts (packed uint256 of BigMath numbers as in storage)\n /// and exchange prices (packed uint256 as in storage).\n /// @param user protocol that triggered this operation (e.g. via an fToken or via Vault protocol)\n /// @param token token address for which this operation was executed\n /// @param supplyAmount supply amount for the operation. if >0 then a deposit happened, if <0 then a withdrawal happened.\n /// if 0 then nothing.\n /// @param borrowAmount borrow amount for the operation. if >0 then a borrow happened, if <0 then a payback happened.\n /// if 0 then nothing.\n /// @param withdrawTo address that funds where withdrawn to (if supplyAmount <0)\n /// @param borrowTo address that funds where borrowed to (if borrowAmount >0)\n /// @param totalAmounts updated total amounts, stacked uint256 as written to storage:\n /// First 64 bits => 0- 63 => total supply with interest in raw (totalSupply = totalSupplyRaw * supplyExchangePrice); BigMath: 56 | 8\n /// Next 64 bits => 64-127 => total interest free supply in normal token amount (totalSupply = totalSupply); BigMath: 56 | 8\n /// Next 64 bits => 128-191 => total borrow with interest in raw (totalBorrow = totalBorrowRaw * borrowExchangePrice); BigMath: 56 | 8\n /// Next 64 bits => 192-255 => total interest free borrow in normal token amount (totalBorrow = totalBorrow); BigMath: 56 | 8\n /// @param exchangePricesAndConfig updated exchange prices and configs storage slot. Contains updated supply & borrow exchange price:\n /// First 16 bits => 0- 15 => borrow rate (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 14 bits => 16- 29 => fee on interest from borrowers to lenders (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383). configurable.\n /// Next 14 bits => 30- 43 => last stored utilization (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n /// Next 14 bits => 44- 57 => update on storage threshold (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383). configurable.\n /// Next 33 bits => 58- 90 => last update timestamp (enough until 16 March 2242 -> max value 8589934591)\n /// Next 64 bits => 91-154 => supply exchange price (1e12 -> max value 18_446_744,073709551615)\n /// Next 64 bits => 155-218 => borrow exchange price (1e12 -> max value 18_446_744,073709551615)\n /// Next 1 bit => 219-219 => if 0 then ratio is supplyInterestFree / supplyWithInterest else ratio is supplyWithInterest / supplyInterestFree\n /// Next 14 bits => 220-233 => supplyRatio: supplyInterestFree / supplyWithInterest (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n /// Next 1 bit => 234-234 => if 0 then ratio is borrowInterestFree / borrowWithInterest else ratio is borrowWithInterest / borrowInterestFree\n /// Next 14 bits => 235-248 => borrowRatio: borrowInterestFree / borrowWithInterest (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n event LogOperate(\n address indexed user,\n address indexed token,\n int256 supplyAmount,\n int256 borrowAmount,\n address withdrawTo,\n address borrowTo,\n uint256 totalAmounts,\n uint256 exchangePricesAndConfig\n );\n}\n" }, "contracts/liquidity/userModule/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/interfaces/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { FixedPointMathLib } from \"solmate/src/utils/FixedPointMathLib.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { BigMathMinified } from \"../../libraries/bigMathMinified.sol\";\nimport { LiquidityCalcs } from \"../../libraries/liquidityCalcs.sol\";\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { CommonHelpers } from \"../common/helpers.sol\";\nimport { Events } from \"./events.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\ninterface IProtocol {\n function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external;\n}\n\nabstract contract CoreInternals is Error, CommonHelpers, Events {\n using BigMathMinified for uint256;\n\n /// @dev supply or withdraw for both with interest & interest free.\n /// positive `amount_` is deposit, negative `amount_` is withdraw.\n function _supplyOrWithdraw(\n address token_,\n int256 amount_,\n uint256 supplyExchangePrice_\n ) internal returns (int256 newSupplyInterestRaw_, int256 newSupplyInterestFree_) {\n uint256 userSupplyData_ = _userSupplyData[msg.sender][token_];\n\n if (userSupplyData_ == 0) {\n revert FluidLiquidityError(ErrorTypes.UserModule__UserNotDefined);\n }\n if ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_IS_PAUSED) & 1 == 1) {\n revert FluidLiquidityError(ErrorTypes.UserModule__UserPaused);\n }\n\n // extract user supply amount\n uint256 userSupply_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n userSupply_ = (userSupply_ >> DEFAULT_EXPONENT_SIZE) << (userSupply_ & DEFAULT_EXPONENT_MASK);\n\n // calculate current, updated (expanded etc.) withdrawal limit\n uint256 newWithdrawalLimit_ = LiquidityCalcs.calcWithdrawalLimitBeforeOperate(userSupplyData_, userSupply_);\n\n // calculate updated user supply amount\n if (userSupplyData_ & 1 == 1) {\n // mode: with interest\n if (amount_ > 0) {\n // convert amount from normal to raw (divide by exchange price) -> round down for deposit\n newSupplyInterestRaw_ = (amount_ * int256(EXCHANGE_PRICES_PRECISION)) / int256(supplyExchangePrice_);\n userSupply_ = userSupply_ + uint256(newSupplyInterestRaw_);\n } else {\n // convert amount from normal to raw (divide by exchange price) -> round up for withdraw\n newSupplyInterestRaw_ = -int256(\n FixedPointMathLib.mulDivUp(uint256(-amount_), EXCHANGE_PRICES_PRECISION, supplyExchangePrice_)\n );\n // if withdrawal is more than user's supply then solidity will throw here\n userSupply_ = userSupply_ - uint256(-newSupplyInterestRaw_);\n }\n } else {\n // mode: without interest\n newSupplyInterestFree_ = amount_;\n if (newSupplyInterestFree_ > 0) {\n userSupply_ = userSupply_ + uint256(newSupplyInterestFree_);\n } else {\n // if withdrawal is more than user's supply then solidity will throw here\n userSupply_ = userSupply_ - uint256(-newSupplyInterestFree_);\n }\n }\n\n if (amount_ < 0 && userSupply_ < newWithdrawalLimit_) {\n // if withdraw, then check the user supply after withdrawal is above withdrawal limit\n revert FluidLiquidityError(ErrorTypes.UserModule__WithdrawalLimitReached);\n }\n\n // calculate withdrawal limit to store as previous withdrawal limit in storage\n newWithdrawalLimit_ = LiquidityCalcs.calcWithdrawalLimitAfterOperate(\n userSupplyData_,\n userSupply_,\n newWithdrawalLimit_\n );\n\n // Converting user's supply into BigNumber\n userSupply_ = userSupply_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n if (((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64) == userSupply_) {\n // make sure that operate amount is not so small that it wouldn't affect storage update. if a difference\n // is present then rounding will be in the right direction to avoid any potential manipulation.\n revert FluidLiquidityError(ErrorTypes.UserModule__OperateAmountInsufficient);\n }\n\n // Converting withdrawal limit into BigNumber\n newWithdrawalLimit_ = newWithdrawalLimit_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n\n // Updating on storage\n _userSupplyData[msg.sender][token_] =\n // mask to update bits 1-161 (supply amount, withdrawal limit, timestamp)\n (userSupplyData_ & 0xfffffffffffffffffffffffc0000000000000000000000000000000000000001) |\n (userSupply_ << LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) | // converted to BigNumber can not overflow\n (newWithdrawalLimit_ << LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) | // converted to BigNumber can not overflow\n (block.timestamp << LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP);\n }\n\n /// @dev borrow or payback for both with interest & interest free.\n /// positive `amount_` is borrow, negative `amount_` is payback.\n function _borrowOrPayback(\n address token_,\n int256 amount_,\n uint256 borrowExchangePrice_\n ) internal returns (int256 newBorrowInterestRaw_, int256 newBorrowInterestFree_) {\n uint256 userBorrowData_ = _userBorrowData[msg.sender][token_];\n\n if (userBorrowData_ == 0) {\n revert FluidLiquidityError(ErrorTypes.UserModule__UserNotDefined);\n }\n if ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_IS_PAUSED) & 1 == 1) {\n revert FluidLiquidityError(ErrorTypes.UserModule__UserPaused);\n }\n\n // extract user borrow amount\n uint256 userBorrow_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64;\n userBorrow_ = (userBorrow_ >> DEFAULT_EXPONENT_SIZE) << (userBorrow_ & DEFAULT_EXPONENT_MASK);\n\n // calculate current, updated (expanded etc.) borrow limit\n uint256 newBorrowLimit_ = LiquidityCalcs.calcBorrowLimitBeforeOperate(userBorrowData_, userBorrow_);\n\n // calculate updated user borrow amount\n if (userBorrowData_ & 1 == 1) {\n // with interest\n if (amount_ > 0) {\n // convert amount normal to raw (divide by exchange price) -> round up for borrow\n newBorrowInterestRaw_ = int256(\n FixedPointMathLib.mulDivUp(uint256(amount_), EXCHANGE_PRICES_PRECISION, borrowExchangePrice_)\n );\n userBorrow_ = userBorrow_ + uint256(newBorrowInterestRaw_);\n } else {\n // convert amount from normal to raw (divide by exchange price) -> round down for payback\n newBorrowInterestRaw_ = (amount_ * int256(EXCHANGE_PRICES_PRECISION)) / int256(borrowExchangePrice_);\n userBorrow_ = userBorrow_ - uint256(-newBorrowInterestRaw_);\n }\n } else {\n // without interest\n newBorrowInterestFree_ = amount_;\n if (newBorrowInterestFree_ > 0) {\n // borrowing\n userBorrow_ = userBorrow_ + uint256(newBorrowInterestFree_);\n } else {\n // payback\n userBorrow_ = userBorrow_ - uint256(-newBorrowInterestFree_);\n }\n }\n\n if (amount_ > 0 && userBorrow_ > newBorrowLimit_) {\n // if borrow, then check the user borrow amount after borrowing is below borrow limit\n revert FluidLiquidityError(ErrorTypes.UserModule__BorrowLimitReached);\n }\n\n // calculate borrow limit to store as previous borrow limit in storage\n newBorrowLimit_ = LiquidityCalcs.calcBorrowLimitAfterOperate(userBorrowData_, userBorrow_, newBorrowLimit_);\n\n // Converting user's borrowings into bignumber\n userBorrow_ = userBorrow_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_UP\n );\n\n if (((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64) == userBorrow_) {\n // make sure that operate amount is not so small that it wouldn't affect storage update. if a difference\n // is present then rounding will be in the right direction to avoid any potential manipulation.\n revert FluidLiquidityError(ErrorTypes.UserModule__OperateAmountInsufficient);\n }\n\n // Converting borrow limit into bignumber\n newBorrowLimit_ = newBorrowLimit_.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n\n // Updating on storage\n _userBorrowData[msg.sender][token_] =\n // mask to update bits 1-161 (borrow amount, borrow limit, timestamp)\n (userBorrowData_ & 0xfffffffffffffffffffffffc0000000000000000000000000000000000000001) |\n (userBorrow_ << LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) | // converted to BigNumber can not overflow\n (newBorrowLimit_ << LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) | // converted to BigNumber can not overflow\n (block.timestamp << LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP);\n }\n\n /// @dev checks if `supplyAmount_` & borrowAmount amounts balance themselves out (checked before calling this method):\n /// - supply(+) == borrow(+), withdraw(-) == payback(-). (DEX protocol use-case)\n /// - `withdrawTo_` / `borrowTo_` must be msg.sender (protocol)\n /// - `callbackData_` MUST be encoded so that \"from\" address is at last 20 bytes (if this optimization is desired),\n /// also for native token operations where liquidityCallback is not triggered!\n /// from address must come at last position if there is more data. I.e. encode like:\n /// abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end\n /// so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end.\n function _isInOutBalancedOut(\n int256 supplyAmount_,\n address withdrawTo_,\n address borrowTo_,\n bytes memory callbackData_\n ) internal view returns (bool) {\n if (callbackData_.length < 20) {\n return false;\n }\n\n address inFrom_;\n assembly {\n inFrom_ := mload(\n add(\n // add padding for length as present for dynamic arrays in memory\n add(callbackData_, 32),\n // assembly expects address with leading zeros / left padded so need to use 32 as length here\n sub(mload(callbackData_), 32)\n )\n )\n }\n return\n // case: supply & borrow. borrow receiver must be the same as depositor and must be protocol\n (supplyAmount_ > 0 && inFrom_ == borrowTo_ && inFrom_ == msg.sender) ||\n // case: withdraw & payback. withdraw receiver must be the same as depositor and must be protocol\n (supplyAmount_ < 0 && inFrom_ == withdrawTo_ && inFrom_ == msg.sender);\n }\n}\n\n/// @title Fluid Liquidity UserModule\n/// @notice Fluid Liquidity public facing endpoint logic contract that implements the `operate()` method.\n/// operate can be used to deposit, withdraw, borrow & payback funds, given that they have the necessary\n/// user config allowance. Interacting users must be allowed via the Fluid Liquidity AdminModule first.\n/// Intended users are thus allow-listed protocols, e.g. the Lending protocol (fTokens), Vault protocol etc.\n/// @dev For view methods / accessing data, use the \"LiquidityResolver\" periphery contract.\ncontract FluidLiquidityUserModule is CoreInternals {\n using SafeERC20 for IERC20;\n using BigMathMinified for uint256;\n\n /// @dev struct for vars used in operate() that would otherwise cause a Stack too deep error\n struct OperateMemoryVars {\n bool skipTransfers;\n uint256 supplyExchangePrice;\n uint256 borrowExchangePrice;\n uint256 supplyRawInterest;\n uint256 supplyInterestFree;\n uint256 borrowRawInterest;\n uint256 borrowInterestFree;\n uint256 totalAmounts;\n uint256 exchangePricesAndConfig;\n }\n\n /// @notice inheritdoc IFluidLiquidity\n function operate(\n address token_,\n int256 supplyAmount_,\n int256 borrowAmount_,\n address withdrawTo_,\n address borrowTo_,\n bytes calldata callbackData_\n ) external payable reentrancy returns (uint256 memVar3_, uint256 memVar4_) {\n if (supplyAmount_ == 0 && borrowAmount_ == 0) {\n revert FluidLiquidityError(ErrorTypes.UserModule__OperateAmountsZero);\n }\n if (\n supplyAmount_ < type(int128).min ||\n supplyAmount_ > type(int128).max ||\n borrowAmount_ < type(int128).min ||\n borrowAmount_ > type(int128).max\n ) {\n revert FluidLiquidityError(ErrorTypes.UserModule__OperateAmountOutOfBounds);\n }\n if ((supplyAmount_ < 0 && withdrawTo_ == address(0)) || (borrowAmount_ > 0 && borrowTo_ == address(0))) {\n revert FluidLiquidityError(ErrorTypes.UserModule__ReceiverNotDefined);\n }\n if (token_ != NATIVE_TOKEN_ADDRESS && msg.value > 0) {\n // revert: there should not be msg.value if the token is not the native token\n revert FluidLiquidityError(ErrorTypes.UserModule__MsgValueForNonNativeToken);\n }\n\n OperateMemoryVars memory o_;\n\n // @dev temporary memory variables used as helper in between to avoid assigning new memory variables\n uint256 memVar_;\n // memVar2_ => operateAmountIn: deposit + payback\n uint256 memVar2_ = uint256((supplyAmount_ > 0 ? supplyAmount_ : int256(0))) +\n uint256((borrowAmount_ < 0 ? -borrowAmount_ : int256(0)));\n // check if in & output amounts balance themselves out:\n // supply(+) == borrow(+), withdraw(-) == payback(-). (DEX protocol use-case)\n // AND msg.value must be 0 otherwise we assume protocol wants normal transfers to happen\n if (\n supplyAmount_ == borrowAmount_ &&\n msg.value == 0 &&\n _isInOutBalancedOut(supplyAmount_, withdrawTo_, borrowTo_, callbackData_)\n ) {\n memVar2_ = 0; // set to 0 to skip transfers IN\n o_.skipTransfers = true; // set flag to true to skip transfers OUT\n }\n if (token_ == NATIVE_TOKEN_ADDRESS) {\n unchecked {\n // check supply and payback amount is covered by available sent msg.value and\n // protection that msg.value is not unintentionally way more than actually used in operate()\n if (memVar2_ > msg.value || msg.value > memVar2_ + NATIVE_AMOUNT_EXCESS_LIMIT) {\n revert FluidLiquidityError(ErrorTypes.UserModule__TransferAmountOutOfBounds);\n }\n }\n memVar2_ = 0; // set to 0 to skip transfers IN more gas efficient. No need for native token.\n }\n // if supply or payback or both -> transfer token amount from sender to here.\n // for native token this is already covered by msg.value checks in operate(). memVar2_ is set to 0\n // for same amounts in same operate(): supply(+) == borrow(+), withdraw(-) == payback(-). memVar2_ is set to 0\n if (memVar2_ > 0) {\n // memVar_ => initial token balance of this contract\n memVar_ = IERC20(token_).balanceOf(address(this));\n // trigger protocol to send token amount and pass callback data\n IProtocol(msg.sender).liquidityCallback(token_, memVar2_, callbackData_);\n // memVar_ => current token balance of this contract - initial balance\n memVar_ = IERC20(token_).balanceOf(address(this)) - memVar_;\n unchecked {\n if (memVar_ < memVar2_ || memVar_ > (memVar2_ + 10)) {\n // revert if protocol did not send enough to cover supply / payback\n // or if protocol sent more than expected, with minor tolerance for any potential rounding issues,\n // even though that should not be needed (liquidityCallback should send exact amount as in params).\n revert FluidLiquidityError(ErrorTypes.UserModule__TransferAmountOutOfBounds);\n }\n }\n }\n\n o_.exchangePricesAndConfig = _exchangePricesAndConfig[token_];\n\n // calculate updated exchange prices\n (o_.supplyExchangePrice, o_.borrowExchangePrice) = LiquidityCalcs.calcExchangePrices(\n o_.exchangePricesAndConfig\n );\n\n // Extract total supply / borrow amounts for the token\n o_.totalAmounts = _totalAmounts[token_];\n memVar_ = o_.totalAmounts & X64;\n o_.supplyRawInterest = (memVar_ >> DEFAULT_EXPONENT_SIZE) << (memVar_ & DEFAULT_EXPONENT_MASK);\n memVar_ = (o_.totalAmounts >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;\n o_.supplyInterestFree = (memVar_ >> DEFAULT_EXPONENT_SIZE) << (memVar_ & DEFAULT_EXPONENT_MASK);\n memVar_ = (o_.totalAmounts >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;\n o_.borrowRawInterest = (memVar_ >> DEFAULT_EXPONENT_SIZE) << (memVar_ & DEFAULT_EXPONENT_MASK);\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n memVar_ = (o_.totalAmounts >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);\n o_.borrowInterestFree = (memVar_ >> DEFAULT_EXPONENT_SIZE) << (memVar_ & DEFAULT_EXPONENT_MASK);\n\n if (supplyAmount_ != 0) {\n // execute supply or withdraw and update total amounts\n {\n uint256 totalAmountsBefore_ = o_.totalAmounts;\n (int256 newSupplyInterestRaw_, int256 newSupplyInterestFree_) = _supplyOrWithdraw(\n token_,\n supplyAmount_,\n o_.supplyExchangePrice\n );\n // update total amounts. this is done here so that values are only written to storage once\n // if a borrow / payback also happens in the same `operate()` call\n if (newSupplyInterestFree_ == 0) {\n // Note newSupplyInterestFree_ can ONLY be 0 if mode is with interest,\n // easy to check as that variable is NOT the result of a dvision etc.\n // supply or withdraw with interest -> raw amount\n if (newSupplyInterestRaw_ > 0) {\n o_.supplyRawInterest += uint256(newSupplyInterestRaw_);\n } else {\n unchecked {\n o_.supplyRawInterest = o_.supplyRawInterest > uint256(-newSupplyInterestRaw_)\n ? o_.supplyRawInterest - uint256(-newSupplyInterestRaw_)\n : 0; // withdraw amount is > total supply -> withdraw total supply down to 0\n // Note no risk here as if the user withdraws more than supplied it would revert already\n // earlier. Total amounts can end up < sum of user amounts because of rounding\n }\n }\n\n // Note check for revert {UserModule}__ValueOverflow__TOTAL_SUPPLY is further down when we anyway\n // calculate the normal amount from raw\n\n // Converting the updated total amount into big number for storage\n memVar_ = o_.supplyRawInterest.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n // update total supply with interest at total amounts in storage (only update changed values)\n o_.totalAmounts =\n // mask to update bits 0-63\n (o_.totalAmounts & 0xffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000) |\n memVar_; // converted to BigNumber can not overflow\n } else {\n // supply or withdraw interest free -> normal amount\n if (newSupplyInterestFree_ > 0) {\n o_.supplyInterestFree += uint256(newSupplyInterestFree_);\n } else {\n unchecked {\n o_.supplyInterestFree = o_.supplyInterestFree > uint256(-newSupplyInterestFree_)\n ? o_.supplyInterestFree - uint256(-newSupplyInterestFree_)\n : 0; // withdraw amount is > total supply -> withdraw total supply down to 0\n // Note no risk here as if the user withdraws more than supplied it would revert already\n // earlier. Total amounts can end up < sum of user amounts because of rounding\n }\n }\n if (o_.supplyInterestFree > MAX_TOKEN_AMOUNT_CAP) {\n // only withdrawals allowed if total supply interest free reaches MAX_TOKEN_AMOUNT_CAP\n revert FluidLiquidityError(ErrorTypes.UserModule__ValueOverflow__TOTAL_SUPPLY);\n }\n // Converting the updated total amount into big number for storage\n memVar_ = o_.supplyInterestFree.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_DOWN\n );\n // update total supply interest free at total amounts in storage (only update changed values)\n o_.totalAmounts =\n // mask to update bits 64-127\n (o_.totalAmounts & 0xffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff) |\n (memVar_ << LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE); // converted to BigNumber can not overflow\n }\n if (totalAmountsBefore_ == o_.totalAmounts) {\n // make sure that operate amount is not so small that it wouldn't affect storage update. if a difference\n // is present then rounding will be in the right direction to avoid any potential manipulation.\n revert FluidLiquidityError(ErrorTypes.UserModule__OperateAmountInsufficient);\n }\n }\n }\n if (borrowAmount_ != 0) {\n // execute borrow or payback and update total amounts\n {\n uint256 totalAmountsBefore_ = o_.totalAmounts;\n (int256 newBorrowInterestRaw_, int256 newBorrowInterestFree_) = _borrowOrPayback(\n token_,\n borrowAmount_,\n o_.borrowExchangePrice\n );\n // update total amounts. this is done here so that values are only written to storage once\n // if a supply / withdraw also happens in the same `operate()` call\n if (newBorrowInterestFree_ == 0) {\n // Note newBorrowInterestFree_ can ONLY be 0 if mode is with interest,\n // easy to check as that variable is NOT the result of a dvision etc.\n // borrow or payback with interest -> raw amount\n if (newBorrowInterestRaw_ > 0) {\n o_.borrowRawInterest += uint256(newBorrowInterestRaw_);\n } else {\n unchecked {\n o_.borrowRawInterest = o_.borrowRawInterest > uint256(-newBorrowInterestRaw_)\n ? o_.borrowRawInterest - uint256(-newBorrowInterestRaw_)\n : 0; // payback amount is > total borrow -> payback total borrow down to 0\n }\n }\n\n // Note check for revert UserModule__ValueOverflow__TOTAL_BORROW is further down when we anyway\n // calculate the normal amount from raw\n\n // Converting the updated total amount into big number for storage\n memVar_ = o_.borrowRawInterest.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_UP\n );\n // update total borrow with interest at total amounts in storage (only update changed values)\n o_.totalAmounts =\n // mask to update bits 128-191\n (o_.totalAmounts & 0xffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff) |\n (memVar_ << LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST); // converted to BigNumber can not overflow\n } else {\n // borrow or payback interest free -> normal amount\n if (newBorrowInterestFree_ > 0) {\n o_.borrowInterestFree += uint256(newBorrowInterestFree_);\n } else {\n unchecked {\n o_.borrowInterestFree = o_.borrowInterestFree > uint256(-newBorrowInterestFree_)\n ? o_.borrowInterestFree - uint256(-newBorrowInterestFree_)\n : 0; // payback amount is > total borrow -> payback total borrow down to 0\n }\n }\n if (o_.borrowInterestFree > MAX_TOKEN_AMOUNT_CAP) {\n // only payback allowed if total borrow interest free reaches MAX_TOKEN_AMOUNT_CAP\n revert FluidLiquidityError(ErrorTypes.UserModule__ValueOverflow__TOTAL_BORROW);\n }\n // Converting the updated total amount into big number for storage\n memVar_ = o_.borrowInterestFree.toBigNumber(\n DEFAULT_COEFFICIENT_SIZE,\n DEFAULT_EXPONENT_SIZE,\n BigMathMinified.ROUND_UP\n );\n // update total borrow interest free at total amounts in storage (only update changed values)\n o_.totalAmounts =\n // mask to update bits 192-255\n (o_.totalAmounts & 0x0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff) |\n (memVar_ << LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE); // converted to BigNumber can not overflow\n }\n if (totalAmountsBefore_ == o_.totalAmounts) {\n // make sure that operate amount is not so small that it wouldn't affect storage update. if a difference\n // is present then rounding will be in the right direction to avoid any potential manipulation.\n revert FluidLiquidityError(ErrorTypes.UserModule__OperateAmountInsufficient);\n }\n }\n }\n // Updating total amounts on storage\n _totalAmounts[token_] = o_.totalAmounts;\n {\n // update exchange prices / utilization / ratios\n // exchangePricesAndConfig is only written to storage if either utilization, supplyRatio or borrowRatio\n // change is above the required storageUpdateThreshold config value or if the last write was > 1 day ago.\n\n // 1. calculate new supply ratio, borrow ratio & utilization.\n // 2. check if last storage write was > 1 day ago.\n // 3. If false -> check if utilization is above update threshold\n // 4. If false -> check if supply ratio is above update threshold\n // 5. If false -> check if borrow ratio is above update threshold\n // 6. If any true, then update on storage\n\n // ########## calculating supply ratio ##########\n // supplyWithInterest in normal amount\n memVar3_ = ((o_.supplyRawInterest * o_.supplyExchangePrice) / EXCHANGE_PRICES_PRECISION);\n if (memVar3_ > MAX_TOKEN_AMOUNT_CAP && supplyAmount_ > 0) {\n // only withdrawals allowed if total supply raw reaches MAX_TOKEN_AMOUNT_CAP\n revert FluidLiquidityError(ErrorTypes.UserModule__ValueOverflow__TOTAL_SUPPLY);\n }\n // memVar_ => total supply. set here so supplyWithInterest (memVar3_) is only calculated once. For utilization\n memVar_ = o_.supplyInterestFree + memVar3_;\n if (memVar3_ > o_.supplyInterestFree) {\n // memVar3_ is ratio with 1 bit as 0 as supply interest raw is bigger\n memVar3_ = ((o_.supplyInterestFree * FOUR_DECIMALS) / memVar3_) << 1;\n // because of checking to divide by bigger amount, ratio can never be > 100%\n } else if (memVar3_ < o_.supplyInterestFree) {\n // memVar3_ is ratio with 1 bit as 1 as supply interest free is bigger\n memVar3_ = (((memVar3_ * FOUR_DECIMALS) / o_.supplyInterestFree) << 1) | 1;\n // because of checking to divide by bigger amount, ratio can never be > 100%\n } else if (memVar_ > 0) {\n // supplies match exactly (memVar3_ == o_.supplyInterestFree) and total supplies are not 0\n // -> set ratio to 1 (with first bit set to 0, doesn't matter)\n memVar3_ = FOUR_DECIMALS << 1;\n } // else if total supply = 0, memVar3_ (supplyRatio) is already 0.\n\n // ########## calculating borrow ratio ##########\n // borrowWithInterest in normal amount\n memVar4_ = ((o_.borrowRawInterest * o_.borrowExchangePrice) / EXCHANGE_PRICES_PRECISION);\n if (memVar4_ > MAX_TOKEN_AMOUNT_CAP && borrowAmount_ > 0) {\n // only payback allowed if total borrow raw reaches MAX_TOKEN_AMOUNT_CAP\n revert FluidLiquidityError(ErrorTypes.UserModule__ValueOverflow__TOTAL_BORROW);\n }\n // memVar2_ => total borrow. set here so borrowWithInterest (memVar4_) is only calculated once. For utilization\n memVar2_ = o_.borrowInterestFree + memVar4_;\n if (memVar4_ > o_.borrowInterestFree) {\n // memVar4_ is ratio with 1 bit as 0 as borrow interest raw is bigger\n memVar4_ = ((o_.borrowInterestFree * FOUR_DECIMALS) / memVar4_) << 1;\n // because of checking to divide by bigger amount, ratio can never be > 100%\n } else if (memVar4_ < o_.borrowInterestFree) {\n // memVar4_ is ratio with 1 bit as 1 as borrow interest free is bigger\n memVar4_ = (((memVar4_ * FOUR_DECIMALS) / o_.borrowInterestFree) << 1) | 1;\n // because of checking to divide by bigger amount, ratio can never be > 100%\n } else if (memVar2_ > 0) {\n // borrows match exactly (memVar4_ == o_.borrowInterestFree) and total borrows are not 0\n // -> set ratio to 1 (with first bit set to 0, doesn't matter)\n memVar4_ = FOUR_DECIMALS << 1;\n } // else if total borrow = 0, memVar4_ (borrowRatio) is already 0.\n\n // calculate utilization. If there is no supply, utilization must be 0 (avoid division by 0)\n uint256 utilization_;\n if (memVar_ > 0) {\n utilization_ = ((memVar2_ * FOUR_DECIMALS) / memVar_);\n\n // for borrow operations, ensure max utilization is not reached\n if (borrowAmount_ > 0) {\n // memVar_ => max utilization\n // if any max utilization other than 100% is set, the flag usesConfigs2 in\n // exchangePricesAndConfig is 1. (optimized to avoid SLOAD if not needed).\n memVar_ = (o_.exchangePricesAndConfig >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_USES_CONFIGS2) &\n 1 ==\n 1\n ? (_configs2[token_] & X14) // read configured max utilization\n : FOUR_DECIMALS; // default max utilization = 100%\n\n if (utilization_ > memVar_) {\n revert FluidLiquidityError(ErrorTypes.UserModule__MaxUtilizationReached);\n }\n }\n }\n\n // check if time difference is big enough (> 1 day)\n unchecked {\n if (\n block.timestamp >\n // extract last update timestamp + 1 day\n (((o_.exchangePricesAndConfig >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33) +\n FORCE_STORAGE_WRITE_AFTER_TIME)\n ) {\n memVar_ = 1; // set write to storage flag\n } else {\n memVar_ = 0;\n }\n }\n\n if (memVar_ == 0) {\n // time difference is not big enough to cause storage write -> check utilization\n\n // memVar_ => extract last utilization\n memVar_ = (o_.exchangePricesAndConfig >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14;\n // memVar2_ => storage update threshold in percent\n memVar2_ =\n (o_.exchangePricesAndConfig >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD) &\n X14;\n unchecked {\n // set memVar_ to 1 if current utilization to previous utilization difference is > update storage threshold\n memVar_ = (utilization_ > memVar_ ? utilization_ - memVar_ : memVar_ - utilization_) > memVar2_\n ? 1\n : 0;\n if (memVar_ == 0) {\n // utilization & time difference is not big enough -> check supplyRatio difference\n // memVar_ => extract last supplyRatio\n memVar_ =\n (o_.exchangePricesAndConfig >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) &\n X15;\n // set memVar_ to 1 if current supplyRatio to previous supplyRatio difference is > update storage threshold\n if ((memVar_ & 1) == (memVar3_ & 1)) {\n memVar_ = memVar_ >> 1;\n memVar_ = (\n (memVar3_ >> 1) > memVar_ ? (memVar3_ >> 1) - memVar_ : memVar_ - (memVar3_ >> 1)\n ) > memVar2_\n ? 1\n : 0; // memVar3_ = supplyRatio, memVar_ = previous supplyRatio, memVar2_ = update storage threshold\n } else {\n // if inverse bit is changing then always update on storage\n memVar_ = 1;\n }\n if (memVar_ == 0) {\n // utilization, time, and supplyRatio difference is not big enough -> check borrowRatio difference\n // memVar_ => extract last borrowRatio\n memVar_ =\n (o_.exchangePricesAndConfig >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) &\n X15;\n // set memVar_ to 1 if current borrowRatio to previous borrowRatio difference is > update storage threshold\n if ((memVar_ & 1) == (memVar4_ & 1)) {\n memVar_ = memVar_ >> 1;\n memVar_ = (\n (memVar4_ >> 1) > memVar_ ? (memVar4_ >> 1) - memVar_ : memVar_ - (memVar4_ >> 1)\n ) > memVar2_\n ? 1\n : 0; // memVar4_ = borrowRatio, memVar_ = previous borrowRatio, memVar2_ = update storage threshold\n } else {\n // if inverse bit is changing then always update on storage\n memVar_ = 1;\n }\n }\n }\n }\n }\n\n // memVar_ is 1 if either time diff was enough or if\n // utilization, supplyRatio or borrowRatio difference was > update storage threshold\n if (memVar_ == 1) {\n // memVar_ => calculate new borrow rate for utilization. includes value overflow check.\n memVar_ = LiquidityCalcs.calcBorrowRateFromUtilization(_rateData[token_], utilization_);\n // ensure values written to storage do not exceed the dedicated bit space in packed uint256 slots\n if (o_.supplyExchangePrice > X64 || o_.borrowExchangePrice > X64) {\n revert FluidLiquidityError(ErrorTypes.UserModule__ValueOverflow__EXCHANGE_PRICES);\n }\n if (utilization_ > X14) {\n revert FluidLiquidityError(ErrorTypes.UserModule__ValueOverflow__UTILIZATION);\n }\n o_.exchangePricesAndConfig =\n (o_.exchangePricesAndConfig &\n // mask to update bits: 0-15 (borrow rate), 30-43 (utilization), 58-248 (timestamp, exchange prices, ratios)\n 0xfe000000000000000000000000000000000000000000000003fff0003fff0000) |\n memVar_ | // calcBorrowRateFromUtilization already includes an overflow check\n (utilization_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) |\n (block.timestamp << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) |\n (o_.supplyExchangePrice << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) |\n (o_.borrowExchangePrice << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) |\n // ratios can never be > 100%, no overflow check needed\n (memVar3_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) | // supplyRatio (memVar3_ holds that value)\n (memVar4_ << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO); // borrowRatio (memVar4_ holds that value)\n // Updating on storage\n _exchangePricesAndConfig[token_] = o_.exchangePricesAndConfig;\n } else {\n // do not update in storage but update o_.exchangePricesAndConfig for updated exchange prices at\n // event emit of LogOperate\n o_.exchangePricesAndConfig =\n (o_.exchangePricesAndConfig &\n // mask to update bits: 91-218 (exchange prices)\n 0xfffffffffc00000000000000000000000000000007ffffffffffffffffffffff) |\n (o_.supplyExchangePrice << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) |\n (o_.borrowExchangePrice << LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE);\n }\n }\n // sending tokens to user at the end after updating everything\n // only transfer to user in case of withdraw or borrow.\n // do not transfer for same amounts in same operate(): supply(+) == borrow(+), withdraw(-) == payback(-). (DEX protocol use-case)\n if ((supplyAmount_ < 0 || borrowAmount_ > 0) && !o_.skipTransfers) {\n // sending tokens to user at the end after updating everything\n // set memVar2_ to borrowAmount (if borrow) or reset memVar2_ var to 0 because\n // it is used with > 0 check below to transfer withdraw / borrow / both\n memVar2_ = borrowAmount_ > 0 ? uint256(borrowAmount_) : 0;\n if (supplyAmount_ < 0) {\n unchecked {\n memVar_ = uint256(-supplyAmount_);\n }\n } else {\n memVar_ = 0;\n }\n if (memVar_ > 0 && memVar2_ > 0 && withdrawTo_ == borrowTo_) {\n // if user is doing borrow & withdraw together and address for both is the same\n // then transfer tokens of borrow & withdraw together to save on gas\n if (token_ == NATIVE_TOKEN_ADDRESS) {\n Address.sendValue(payable(withdrawTo_), memVar_ + memVar2_);\n } else {\n IERC20(token_).safeTransfer(withdrawTo_, memVar_ + memVar2_);\n }\n } else {\n if (token_ == NATIVE_TOKEN_ADDRESS) {\n // if withdraw\n if (memVar_ > 0) {\n Address.sendValue(payable(withdrawTo_), memVar_);\n }\n // if borrow\n if (memVar2_ > 0) {\n Address.sendValue(payable(borrowTo_), memVar2_);\n }\n } else {\n // if withdraw\n if (memVar_ > 0) {\n IERC20(token_).safeTransfer(withdrawTo_, memVar_);\n }\n // if borrow\n if (memVar2_ > 0) {\n IERC20(token_).safeTransfer(borrowTo_, memVar2_);\n }\n }\n }\n }\n // emit Operate event\n emit LogOperate(\n msg.sender,\n token_,\n supplyAmount_,\n borrowAmount_,\n withdrawTo_,\n borrowTo_,\n o_.totalAmounts,\n o_.exchangePricesAndConfig\n );\n // set return values\n memVar3_ = o_.supplyExchangePrice;\n memVar4_ = o_.borrowExchangePrice;\n }\n}\n" }, "contracts/mocks/emptyImplementation.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract EmptyImplementation {}\n" }, "contracts/mocks/emptyImplementationUUPS.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { UUPSUpgradeable } from \"@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol\";\n\n/// @dev generic empty contract that can be set for UUPS proxies as initial logic contract, to avoid it affecting\n/// the deterministic contract address. upgrade is auth-gated via an immutable owner set in constructor, to\n/// not affect the storage layout.\ncontract EmptyImplementationUUPS is UUPSUpgradeable {\n /// @dev used to auth-gate upgrade triggering\n address public immutable OWNER;\n\n error Unauthorized();\n\n constructor(address owner_) {\n OWNER = owner_;\n }\n\n function _authorizeUpgrade(address) internal override {\n if (msg.sender != OWNER) {\n revert Unauthorized();\n }\n }\n}\n" }, "contracts/mocks/mockERC721.sol": { "content": "pragma solidity 0.8.21;\n\nimport { IERC721Enumerable } from \"@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol\";\n\ncontract MockonERC721Received {\n error ERC721Error();\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4) {\n if (data.length != 0) {\n address owner = abi.decode(data, (address));\n\n require(owner == operator, \"owner-not-same\");\n if (operator != from) revert ERC721Error();\n\n address child = address(new MockonERC721ReceivedChild());\n\n // Transfer to Child contract\n IERC721Enumerable(msg.sender).safeTransferFrom(address(this), child, tokenId, data);\n\n // Transfer to owner back.\n IERC721Enumerable(msg.sender).safeTransferFrom(address(this), operator, tokenId);\n }\n\n return MockonERC721Received(address(this)).onERC721Received.selector;\n }\n}\n\ncontract MockonERC721ReceivedChild {\n error ERC721Error();\n\n address immutable FACTORY;\n constructor () {\n FACTORY = msg.sender;\n }\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4) {\n (address toTransfer) = abi.decode(data, (address));\n\n if (operator != from) revert ERC721Error();\n\n IERC721Enumerable(msg.sender).safeTransferFrom(address(this), FACTORY, tokenId);\n\n return MockonERC721Received(address(this)).onERC721Received.selector;\n }\n}\n\n" }, "contracts/mocks/mockFLA.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/interfaces/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ninterface InstaFlashInterface {\n function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external;\n}\n\ninterface InstaFlashReceiverInterface {\n function executeOperation(\n address[] calldata assets,\n uint256[] calldata amounts,\n uint256[] calldata premiums,\n address initiator,\n bytes calldata _data\n ) external returns (bool);\n}\n\ncontract MockFLA {\n using SafeERC20 for IERC20;\n \n function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external {\n uint256[] memory premiums = new uint256[](1);\n premiums[0] = 0;\n IERC20(tokens[0]).safeTransfer(msg.sender, amts[0]);\n InstaFlashReceiverInterface(msg.sender).executeOperation(\n tokens,\n amts,\n premiums,\n msg.sender,\n data\n );\n }\n}" }, "contracts/mocks/mockOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../oracle/fluidOracle.sol\";\n\n/// @notice Mock Oracle for testing\ncontract MockOracle is FluidOracle {\n uint256 public price;\n\n // Price is in 1e27 decimals between 2 tokens.\n // For example: if 1 ETH = 2000 USDC, that means 1e18 of ETH = 2000 * 1e6 of USDC\n // debt per col = 2000 * 1e6 * 1e27 / 1e18;\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() external view override returns (uint256 exchangeRate_) {\n return price;\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return price;\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return price;\n }\n\n function setPrice(uint256 price_) external {\n price = price_;\n }\n}\n" }, "contracts/mocks/mockProtocol.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { FluidLiquidityUserModule } from \"../liquidity/userModule/main.sol\";\nimport { IFluidLiquidity } from \"../liquidity/interfaces/iLiquidity.sol\";\n\n/// @title Mock Protocol\n/// @notice Mock protocol for testing, implements:\n/// function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external;\n/// This callback method MUST transferFrom data_ decoded from address to the liquidity contract\ncontract MockProtocol {\n using SafeERC20 for IERC20;\n\n /// @dev address that is mapped to the chain native token\n address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n address public immutable liquidityContract;\n\n /// @notice thrown when liquidity callback is called for a native token operation\n error MockProtocol__UnexpectedLiquidityCallback();\n\n bool transferInsufficientMode = false;\n bool transferExcessMode = false;\n bool reentrancyFromCallback = false;\n address transferFromAddress;\n\n /// @notice Construct a new MockProtocol\n /// @param liquidityContract_ The address of the liquidity contract\n constructor(address liquidityContract_) {\n liquidityContract = liquidityContract_;\n }\n\n receive() external payable {}\n\n function setTransferInsufficientMode(bool transferInsufficientMode_) public {\n transferInsufficientMode = transferInsufficientMode_;\n }\n\n function setTransferExcessMode(bool transferExcessMode_) public {\n transferExcessMode = transferExcessMode_;\n }\n\n function setReentrancyFromCallback(bool reentrancyFromCallback_) public {\n reentrancyFromCallback = reentrancyFromCallback_;\n }\n\n function setTransferFromAddress(address transferFromAddress_) public {\n transferFromAddress = transferFromAddress_;\n }\n\n /// @notice Mock liquidity callback\n /// @param token_ The token being transferred\n /// @param amount_ The amount being transferred\n function liquidityCallback(address token_, uint256 amount_, bytes memory data_) external {\n if (reentrancyFromCallback) {\n // call operate with some random values (should not matter as it reverts anyway)\n IFluidLiquidity(liquidityContract).operate(token_, 10, 0, address(0), address(0), abi.encode(address(this)));\n }\n\n if (token_ == NATIVE_TOKEN_ADDRESS) {\n revert MockProtocol__UnexpectedLiquidityCallback();\n }\n\n address from_;\n if (transferFromAddress == address(0)) {\n // take the last 20 bytes of data_ and decode them to address. Gives more flexibility in type of\n // data that can be passed in to Liquidity at mock calls while ensuring mock Protocol can do what it\n // is supposed to do: transfer amount of token to liquidity.\n assembly {\n from_ := mload(\n add(\n // add padding for length as present for dynamic arrays in memory\n add(data_, 32),\n // assembly expects address with leading zeros / left padded so need to use 32 as length here\n sub(mload(data_), 32)\n )\n )\n }\n } else {\n from_ = transferFromAddress;\n }\n\n if (amount_ > 0) {\n if (transferExcessMode) {\n amount_ += 1e6;\n } else if (transferInsufficientMode) {\n amount_ -= 1;\n }\n }\n\n if (from_ == address(this)) {\n // use approve and transferFrom for more consistent testing of methods called\n // (always transferFrom instead of transfer)\n IERC20(token_).safeApprove(address(this), amount_);\n IERC20(token_).safeTransferFrom(address(this), liquidityContract, amount_);\n } else {\n IERC20(token_).safeTransferFrom(from_, liquidityContract, amount_);\n }\n }\n\n /// @notice Proxy method for executing `operate` on the liquidity contract\n function operate(\n address token_,\n int256 supplyAmount_,\n int256 borrowAmount_,\n address withdrawTo_,\n address borrowTo_,\n bytes calldata callbackData_\n ) external payable returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n uint256 valueAmount = msg.value;\n\n if (valueAmount > 0) {\n if (transferExcessMode) {\n valueAmount += 1e9 + 1;\n } else if (transferInsufficientMode) {\n valueAmount -= 1;\n }\n }\n\n return\n FluidLiquidityUserModule(liquidityContract).operate{ value: valueAmount }(\n token_,\n supplyAmount_,\n borrowAmount_,\n withdrawTo_,\n borrowTo_,\n callbackData_\n );\n }\n}\n" }, "contracts/mocks/mockSwap.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/interfaces/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\ncontract MockSwap {\n using SafeERC20 for IERC20;\n \n function swap(address buy_, address sell_, uint256 buyAmount_, uint256 sellAmount_) external payable {\n if (sell_ == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {\n // nothing\n } else {\n IERC20(sell_).safeTransferFrom(msg.sender, address(this), sellAmount_);\n }\n\n if (buy_ == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {\n Address.sendValue(payable(msg.sender), buyAmount_);\n } else {\n IERC20(buy_).safeTransfer(msg.sender, buyAmount_);\n }\n }\n}" }, "contracts/mocks/mockWETH.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n\ncontract MockWETH is ERC20 {\n constructor () ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 amount) external {\n _burn(msg.sender, amount);\n Address.sendValue(payable(msg.sender), amount);\n }\n}" }, "contracts/oracle/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Error {\n error FluidOracleError(uint256 errorId_);\n}\n" }, "contracts/oracle/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | UniV3CheckCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the delta between main price source and check rate source is exceeding the allowed delta\n uint256 internal constant UniV3CheckCLRSOracle__InvalidPrice = 60001;\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant UniV3CheckCLRSOracle__InvalidParams = 60002;\n\n /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config\n uint256 internal constant UniV3CheckCLRSOracle__ExchangeRateZero = 60003;\n\n /***********************************|\n | sUSDe Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant SUSDeOracle__InvalidParams = 60102;\n\n /***********************************|\n | Chainlink Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant ChainlinkOracle__InvalidParams = 61001;\n\n /***********************************|\n | UniswapV3 Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant UniV3Oracle__InvalidParams = 62001;\n\n /// @notice thrown when constructor is called with invalid ordered seconds agos values\n uint256 internal constant UniV3Oracle__InvalidSecondsAgos = 62002;\n\n /// @notice thrown when constructor is called with invalid delta values > 100%\n uint256 internal constant UniV3Oracle__InvalidDeltas = 62003;\n\n /***********************************|\n | WstETh Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant WstETHOracle__InvalidParams = 63001;\n\n /***********************************|\n | Redstone Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant RedstoneOracle__InvalidParams = 64001;\n\n /***********************************|\n | Fallback Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant FallbackOracle__InvalidParams = 65001;\n\n /***********************************|\n | FallbackCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled)\n uint256 internal constant FallbackCLRSOracle__ExchangeRateZero = 66001;\n\n /***********************************|\n | WstETHCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled)\n uint256 internal constant WstETHCLRSOracle__ExchangeRateZero = 67001;\n\n /***********************************|\n | CLFallbackUniV3Oracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the uniV3 rate\n uint256 internal constant CLFallbackUniV3Oracle__ExchangeRateZero = 68001;\n\n /***********************************|\n | WstETHCLRS2UniV3CheckCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the uniV3 rate\n uint256 internal constant WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero = 69001;\n\n /***********************************|\n | WeETh Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant WeETHOracle__InvalidParams = 70001;\n}\n" }, "contracts/oracle/fluidOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidOracle } from \"./interfaces/iFluidOracle.sol\";\n\n/// @title FluidOracle\n/// @notice Base contract that any Fluid Oracle must implement\nabstract contract FluidOracle is IFluidOracle {\n /// @inheritdoc IFluidOracle\n function getExchangeRate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_);\n}\n" }, "contracts/oracle/implementations/chainlinkOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IChainlinkAggregatorV3 } from \"../interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ChainlinkStructs } from \"./structs.sol\";\n\n/// @title Chainlink Oracle implementation\n/// @notice This contract is used to get the exchange rate via up to 3 hops at Chainlink price feeds.\n/// The rate is multiplied with the previous rate at each hop.\n/// E.g. to go from wBTC to USDC (assuming rates for example):\n/// 1. wBTC -> BTC https://data.chain.link/ethereum/mainnet/crypto-other/wbtc-btc, rate: 0.92.\n/// 2. BTC -> USD https://data.chain.link/ethereum/mainnet/crypto-usd/btc-usd rate: 30,000.\n/// 3. USD -> USDC https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd rate: 0.98. Must invert feed: 1.02\n/// finale rate would be: 0.92 * 30,000 * 1.02 = 28,152\nabstract contract ChainlinkOracleImpl is OracleError, ChainlinkStructs {\n /// @notice Chainlink price feed 1 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED1;\n /// @notice Chainlink price feed 2 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED2;\n /// @notice Chainlink price feed 3 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED3;\n\n /// @notice Flag to invert the price or not for feed 1 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK_INVERT_RATE1;\n /// @notice Flag to invert the price or not for feed 2 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK_INVERT_RATE2;\n /// @notice Flag to invert the price or not for feed 3 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK_INVERT_RATE3;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 1\n uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER1;\n /// @notice constant value for inverting price to reduce gas usage for feed 1\n uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND1;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 2\n uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER2;\n /// @notice constant value for inverting price to reduce gas usage for feed 2\n uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND2;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 3\n uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER3;\n /// @notice constant value for inverting price to reduce gas usage for feed 3\n uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND3;\n\n /// @notice constructor sets the Chainlink price feed and invertRate flag for each hop.\n /// E.g. `invertRate_` should be true if for the USDC/ETH pool it's expected that the oracle returns USDC per 1 ETH\n constructor(ChainlinkConstructorParams memory params_) {\n if (\n (params_.hops < 1 || params_.hops > 3) || // hops must be 1, 2 or 3\n (address(params_.feed1.feed) == address(0) || params_.feed1.token0Decimals == 0) || // first feed must always be defined\n (params_.hops > 1 && (address(params_.feed2.feed) == address(0) || params_.feed2.token0Decimals == 0)) || // if hops > 1, feed 2 must be defined\n (params_.hops > 2 && (address(params_.feed3.feed) == address(0) || params_.feed3.token0Decimals == 0)) // if hops > 2, feed 3 must be defined\n ) {\n revert FluidOracleError(ErrorTypes.ChainlinkOracle__InvalidParams);\n }\n\n _CHAINLINK_FEED1 = params_.feed1.feed;\n _CHAINLINK_FEED2 = params_.feed2.feed;\n _CHAINLINK_FEED3 = params_.feed3.feed;\n\n _CHAINLINK_INVERT_RATE1 = params_.feed1.invertRate;\n _CHAINLINK_INVERT_RATE2 = params_.feed2.invertRate;\n _CHAINLINK_INVERT_RATE3 = params_.feed3.invertRate;\n\n // Actual desired output rate example USDC/ETH (6 decimals / 18 decimals).\n // Note ETH has 12 decimals more than USDC.\n // 0.000515525322211842331991619857165357691 // 39 decimals. ETH for 1 USDC\n // 1954.190000000000433 // 15 decimals. USDC for 1 ETH\n\n // to get to PRICE_SCLAER_MULTIPLIER and INVERT_PRICE_DIVIDEND:\n // fetched Chainlink price is in token1Decimals per 1 token0Decimals.\n // E.g. for an USDC/ETH price feed it's in ETH 18 decimals.\n // for an BTC/USD price feed it's in USD 8 decimals.\n // So to scale to 1e27 we need to multiply by 1e27 - token0Decimals.\n // E.g. for USDC/ETH it would be: fetchedPrice * 1e21\n //\n // or for inverted (x token0 per 1 token1), formula would be:\n // = 1e27 * 10**token0Decimals / fetchedPrice\n // E.g. for USDC/ETH it would be: 1e33 / fetchedPrice\n\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _CHAINLINK_PRICE_SCALER_MULTIPLIER1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed1.token0Decimals);\n _CHAINLINK_INVERT_PRICE_DIVIDEND1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed1.token0Decimals);\n\n _CHAINLINK_PRICE_SCALER_MULTIPLIER2 = params_.hops > 1\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed2.token0Decimals)\n : 1;\n _CHAINLINK_INVERT_PRICE_DIVIDEND2 = params_.hops > 1\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed2.token0Decimals)\n : 1;\n\n _CHAINLINK_PRICE_SCALER_MULTIPLIER3 = params_.hops > 2\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed3.token0Decimals)\n : 1;\n _CHAINLINK_INVERT_PRICE_DIVIDEND3 = params_.hops > 2\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed3.token0Decimals)\n : 1;\n }\n\n /// @dev Get the exchange rate from Chainlike oracle price feed(s)\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getChainlinkExchangeRate() internal view returns (uint256 rate_) {\n rate_ = _readFeedRate(\n _CHAINLINK_FEED1,\n _CHAINLINK_INVERT_RATE1,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER1,\n _CHAINLINK_INVERT_PRICE_DIVIDEND1\n );\n if (rate_ == 0 || address(_CHAINLINK_FEED2) == address(0)) {\n // rate 0 or only 1 hop -> return rate of price feed 1\n return rate_;\n }\n rate_ =\n (rate_ *\n _readFeedRate(\n _CHAINLINK_FEED2,\n _CHAINLINK_INVERT_RATE2,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER2,\n _CHAINLINK_INVERT_PRICE_DIVIDEND2\n )) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n if (rate_ == 0 || address(_CHAINLINK_FEED3) == address(0)) {\n // rate 0 or 2 hops -> return rate of feed 1 combined with feed 2\n return rate_;\n }\n\n // 3 hops -> return rate of feed 1 combined with feed 2 & feed 3\n rate_ =\n (rate_ *\n _readFeedRate(\n _CHAINLINK_FEED3,\n _CHAINLINK_INVERT_RATE3,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER3,\n _CHAINLINK_INVERT_PRICE_DIVIDEND3\n )) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @dev reads the exchange `rate_` from a Chainlink price `feed_` taking into account scaling and `invertRate_`\n function _readFeedRate(\n IChainlinkAggregatorV3 feed_,\n bool invertRate_,\n uint256 priceMultiplier_,\n uint256 invertDividend_\n ) private view returns (uint256 rate_) {\n try feed_.latestRoundData() returns (uint80, int256 exchangeRate_, uint256, uint256, uint80) {\n // Return the price in `OracleUtils.RATE_OUTPUT_DECIMALS`\n if (invertRate_) {\n return invertDividend_ / uint256(exchangeRate_);\n } else {\n return uint256(exchangeRate_) * priceMultiplier_;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Chainlink oracle related data as utility for easy off-chain use / block explorer in a single view method\n function chainlinkOracleData()\n public\n view\n returns (\n uint256 chainlinkExchangeRate_,\n IChainlinkAggregatorV3 chainlinkFeed1_,\n bool chainlinkInvertRate1_,\n uint256 chainlinkExchangeRate1_,\n IChainlinkAggregatorV3 chainlinkFeed2_,\n bool chainlinkInvertRate2_,\n uint256 chainlinkExchangeRate2_,\n IChainlinkAggregatorV3 chainlinkFeed3_,\n bool chainlinkInvertRate3_,\n uint256 chainlinkExchangeRate3_\n )\n {\n return (\n _getChainlinkExchangeRate(),\n _CHAINLINK_FEED1,\n _CHAINLINK_INVERT_RATE1,\n _readFeedRate(\n _CHAINLINK_FEED1,\n _CHAINLINK_INVERT_RATE1,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER1,\n _CHAINLINK_INVERT_PRICE_DIVIDEND1\n ),\n _CHAINLINK_FEED2,\n _CHAINLINK_INVERT_RATE2,\n address(_CHAINLINK_FEED2) == address(0)\n ? 0\n : _readFeedRate(\n _CHAINLINK_FEED2,\n _CHAINLINK_INVERT_RATE2,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER2,\n _CHAINLINK_INVERT_PRICE_DIVIDEND2\n ),\n _CHAINLINK_FEED3,\n _CHAINLINK_INVERT_RATE3,\n address(_CHAINLINK_FEED3) == address(0)\n ? 0\n : _readFeedRate(\n _CHAINLINK_FEED3,\n _CHAINLINK_INVERT_RATE3,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER3,\n _CHAINLINK_INVERT_PRICE_DIVIDEND3\n )\n );\n }\n}\n" }, "contracts/oracle/implementations/chainlinkOracleImpl2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IChainlinkAggregatorV3 } from \"../interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ChainlinkStructs } from \"./structs.sol\";\n\n// @dev Exact same contract as ChainlinkOracleImpl, just with all vars, immutables etc. renamed with a \"2\" to avoid\n// conflicts when ChainlinkOracleImpl would have to be inherited twice.\n\n/// @title Chainlink Oracle implementation.\n/// @notice This contract is used to get the exchange rate via up to 3 hops at Chainlink price feeds.\n/// The rate is multiplied with the previous rate at each hop.\n/// E.g. to go from wBTC to USDC (assuming rates for example):\n/// 1. wBTC -> BTC https://data.chain.link/ethereum/mainnet/crypto-other/wbtc-btc, rate: 0.92.\n/// 2. BTC -> USD https://data.chain.link/ethereum/mainnet/crypto-usd/btc-usd rate: 30,000.\n/// 3. USD -> USDC https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd rate: 0.98. Must invert feed: 1.02\n/// finale rate would be: 0.92 * 30,000 * 1.02 = 28,152\nabstract contract ChainlinkOracleImpl2 is OracleError, ChainlinkStructs {\n /// @notice Chainlink price feed 1 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK2_FEED1;\n /// @notice Chainlink price feed 2 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK2_FEED2;\n /// @notice Chainlink price feed 3 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK2_FEED3;\n\n /// @notice Flag to invert the price or not for feed 1 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK2_INVERT_RATE1;\n /// @notice Flag to invert the price or not for feed 2 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK2_INVERT_RATE2;\n /// @notice Flag to invert the price or not for feed 3 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK2_INVERT_RATE3;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 1\n uint256 internal immutable _CHAINLINK2_PRICE_SCALER_MULTIPLIER1;\n /// @notice constant value for inverting price to reduce gas usage for feed 1\n uint256 internal immutable _CHAINLINK2_INVERT_PRICE_DIVIDEND1;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 2\n uint256 internal immutable _CHAINLINK2_PRICE_SCALER_MULTIPLIER2;\n /// @notice constant value for inverting price to reduce gas usage for feed 2\n uint256 internal immutable _CHAINLINK2_INVERT_PRICE_DIVIDEND2;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 3\n uint256 internal immutable _CHAINLINK2_PRICE_SCALER_MULTIPLIER3;\n /// @notice constant value for inverting price to reduce gas usage for feed 3\n uint256 internal immutable _CHAINLINK2_INVERT_PRICE_DIVIDEND3;\n\n /// @notice constructor sets the Chainlink price feed and invertRate flag for each hop.\n /// E.g. `invertRate_` should be true if for the USDC/ETH pool it's expected that the oracle returns USDC per 1 ETH\n constructor(ChainlinkConstructorParams memory params_) {\n if (\n (params_.hops < 1 || params_.hops > 3) || // hops must be 1, 2 or 3\n (address(params_.feed1.feed) == address(0) || params_.feed1.token0Decimals == 0) || // first feed must always be defined\n (params_.hops > 1 && (address(params_.feed2.feed) == address(0) || params_.feed2.token0Decimals == 0)) || // if hops > 1, feed 2 must be defined\n (params_.hops > 2 && (address(params_.feed3.feed) == address(0) || params_.feed3.token0Decimals == 0)) // if hops > 2, feed 3 must be defined\n ) {\n revert FluidOracleError(ErrorTypes.ChainlinkOracle__InvalidParams);\n }\n\n _CHAINLINK2_FEED1 = params_.feed1.feed;\n _CHAINLINK2_FEED2 = params_.feed2.feed;\n _CHAINLINK2_FEED3 = params_.feed3.feed;\n\n _CHAINLINK2_INVERT_RATE1 = params_.feed1.invertRate;\n _CHAINLINK2_INVERT_RATE2 = params_.feed2.invertRate;\n _CHAINLINK2_INVERT_RATE3 = params_.feed3.invertRate;\n\n // Actual desired output rate example USDC/ETH (6 decimals / 18 decimals).\n // Note ETH has 12 decimals more than USDC.\n // 0.000515525322211842331991619857165357691 // 39 decimals. ETH for 1 USDC\n // 1954.190000000000433 // 15 decimals. USDC for 1 ETH\n\n // to get to PRICE_SCLAER_MULTIPLIER and INVERT_PRICE_DIVIDEND:\n // fetched Chainlink price is in token1Decimals per 1 token0Decimals.\n // E.g. for an USDC/ETH price feed it's in ETH 18 decimals.\n // for an BTC/USD price feed it's in USD 8 decimals.\n // So to scale to 1e27 we need to multiply by 1e27 - token0Decimals.\n // E.g. for USDC/ETH it would be: fetchedPrice * 1e21\n //\n // or for inverted (x token0 per 1 token1), formula would be:\n // = 1e27 * 10**token0Decimals / fetchedPrice\n // E.g. for USDC/ETH it would be: 1e33 / fetchedPrice\n\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed1.token0Decimals);\n _CHAINLINK2_INVERT_PRICE_DIVIDEND1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed1.token0Decimals);\n\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER2 = params_.hops > 1\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed2.token0Decimals)\n : 1;\n _CHAINLINK2_INVERT_PRICE_DIVIDEND2 = params_.hops > 1\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed2.token0Decimals)\n : 1;\n\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER3 = params_.hops > 2\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed3.token0Decimals)\n : 1;\n _CHAINLINK2_INVERT_PRICE_DIVIDEND3 = params_.hops > 2\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed3.token0Decimals)\n : 1;\n }\n\n /// @dev Get the exchange rate from Chainlike oracle price feed(s)\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getChainlinkExchangeRate2() internal view returns (uint256 rate_) {\n rate_ = _readFeedRate2(\n _CHAINLINK2_FEED1,\n _CHAINLINK2_INVERT_RATE1,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER1,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND1\n );\n if (rate_ == 0 || address(_CHAINLINK2_FEED2) == address(0)) {\n // rate 0 or only 1 hop -> return rate of price feed 1\n return rate_;\n }\n rate_ =\n (rate_ *\n _readFeedRate2(\n _CHAINLINK2_FEED2,\n _CHAINLINK2_INVERT_RATE2,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER2,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND2\n )) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n if (rate_ == 0 || address(_CHAINLINK2_FEED3) == address(0)) {\n // rate 0 or 2 hops -> return rate of feed 1 combined with feed 2\n return rate_;\n }\n\n // 3 hops -> return rate of feed 1 combined with feed 2 & feed 3\n rate_ =\n (rate_ *\n _readFeedRate2(\n _CHAINLINK2_FEED3,\n _CHAINLINK2_INVERT_RATE3,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER3,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND3\n )) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @dev reads the exchange `rate_` from a Chainlink price `feed_` taking into account scaling and `invertRate_`\n function _readFeedRate2(\n IChainlinkAggregatorV3 feed_,\n bool invertRate_,\n uint256 priceMultiplier_,\n uint256 invertDividend_\n ) private view returns (uint256 rate_) {\n try feed_.latestRoundData() returns (uint80, int256 exchangeRate_, uint256, uint256, uint80) {\n // Return the price in `OracleUtils.RATE_OUTPUT_DECIMALS`\n if (invertRate_) {\n return invertDividend_ / uint256(exchangeRate_);\n } else {\n return uint256(exchangeRate_) * priceMultiplier_;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Chainlink oracle related data as utility for easy off-chain use / block explorer in a single view method\n function chainlinkOracleData2()\n public\n view\n returns (\n uint256 chainlinkExchangeRate_,\n IChainlinkAggregatorV3 chainlinkFeed1_,\n bool chainlinkInvertRate1_,\n uint256 chainlinkExchangeRate1_,\n IChainlinkAggregatorV3 chainlinkFeed2_,\n bool chainlinkInvertRate2_,\n uint256 chainlinkExchangeRate2_,\n IChainlinkAggregatorV3 chainlinkFeed3_,\n bool chainlinkInvertRate3_,\n uint256 chainlinkExchangeRate3_\n )\n {\n return (\n _getChainlinkExchangeRate2(),\n _CHAINLINK2_FEED1,\n _CHAINLINK2_INVERT_RATE1,\n _readFeedRate2(\n _CHAINLINK2_FEED1,\n _CHAINLINK2_INVERT_RATE1,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER1,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND1\n ),\n _CHAINLINK2_FEED2,\n _CHAINLINK2_INVERT_RATE2,\n address(_CHAINLINK2_FEED2) == address(0)\n ? 0\n : _readFeedRate2(\n _CHAINLINK2_FEED2,\n _CHAINLINK2_INVERT_RATE2,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER2,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND2\n ),\n _CHAINLINK2_FEED3,\n _CHAINLINK2_INVERT_RATE3,\n address(_CHAINLINK2_FEED3) == address(0)\n ? 0\n : _readFeedRate2(\n _CHAINLINK2_FEED3,\n _CHAINLINK2_INVERT_RATE3,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER3,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND3\n )\n );\n }\n}\n" }, "contracts/oracle/implementations/fallbackOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { ChainlinkOracleImpl } from \"./chainlinkOracleImpl.sol\";\nimport { RedstoneOracleImpl } from \"./redstoneOracleImpl.sol\";\n\n/// @title Fallback Oracle implementation\n/// @notice This contract is used to get the exchange rate from a main oracle feed and a fallback oracle feed.\n//\n// @dev inheriting contracts should implement a view method to expose `_FALLBACK_ORACLE_MAIN_SOURCE`\nabstract contract FallbackOracleImpl is OracleError, RedstoneOracleImpl, ChainlinkOracleImpl {\n /// @dev which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n uint8 internal immutable _FALLBACK_ORACLE_MAIN_SOURCE;\n\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param mainSource_ which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n )\n ChainlinkOracleImpl(chainlinkParams_)\n RedstoneOracleImpl(\n address(redstoneOracle_.oracle) == address(0)\n ? RedstoneOracleData(IRedstoneOracle(_REDSTONE_ORACLE_NOT_SET_ADDRESS), false, 1)\n : redstoneOracle_\n )\n {\n if (mainSource_ < 1 || mainSource_ > 3) {\n revert FluidOracleError(ErrorTypes.FallbackOracle__InvalidParams);\n }\n _FALLBACK_ORACLE_MAIN_SOURCE = mainSource_;\n }\n\n /// @dev returns the exchange rate for the main oracle source, or the fallback source (if configured) if the main exchange rate\n /// fails to be fetched. If returned rate is 0, fetching rate failed or something went wrong.\n /// @return exchangeRate_ exchange rate\n /// @return fallback_ whether fallback was necessary or not\n function _getRateWithFallback() internal view returns (uint256 exchangeRate_, bool fallback_) {\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 1) {\n // 1 = Chainlink ONLY (no fallback)\n exchangeRate_ = _getChainlinkExchangeRate();\n } else if (_FALLBACK_ORACLE_MAIN_SOURCE == 2) {\n // 2 = Chainlink with Redstone Fallback\n exchangeRate_ = _getChainlinkExchangeRate();\n if (exchangeRate_ == 0) {\n fallback_ = true;\n exchangeRate_ = _getRedstoneExchangeRate();\n }\n } else {\n // 3 = Redstone with Chainlink Fallback\n exchangeRate_ = _getRedstoneExchangeRate();\n if (exchangeRate_ == 0) {\n fallback_ = true;\n exchangeRate_ = _getChainlinkExchangeRate();\n }\n }\n }\n\n /// @dev returns the exchange rate for Chainlink, or Redstone if configured & Chainlink fails.\n function _getChainlinkOrRedstoneAsFallback() internal view returns (uint256 exchangeRate_) {\n exchangeRate_ = _getChainlinkExchangeRate();\n\n if (exchangeRate_ == 0 && _FALLBACK_ORACLE_MAIN_SOURCE != 1) {\n // Chainlink failed but Redstone is configured too -> try Redstone\n exchangeRate_ = _getRedstoneExchangeRate();\n }\n }\n}\n" }, "contracts/oracle/implementations/fallbackOracleImpl2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { ChainlinkOracleImpl2 } from \"./chainlinkOracleImpl2.sol\";\nimport { RedstoneOracleImpl2 } from \"./redstoneOracleImpl2.sol\";\n\n// @dev Exact same contract as FallbackOracleImpl, just with all vars, immutables etc. renamed with a \"2\" and inheriting\n// to ChainlinkOracleImpl2 and RedstoneOracleImpl2 to avoid conflicts when FallbackOracleImpl would have to be inherited twice.\n\n/// @title Fallback Oracle implementation\n/// @notice This contract is used to get the exchange rate from a main oracle feed and a fallback oracle feed.\n//\n// @dev inheriting contracts should implement a view method to expose `_FALLBACK_ORACLE2_MAIN_SOURCE`\nabstract contract FallbackOracleImpl2 is OracleError, RedstoneOracleImpl2, ChainlinkOracleImpl2 {\n /// @dev which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n uint8 internal immutable _FALLBACK_ORACLE2_MAIN_SOURCE;\n\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param mainSource_ which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n )\n ChainlinkOracleImpl2(chainlinkParams_)\n RedstoneOracleImpl2(\n address(redstoneOracle_.oracle) == address(0)\n ? RedstoneOracleData(IRedstoneOracle(_REDSTONE2_ORACLE_NOT_SET_ADDRESS), false, 1)\n : redstoneOracle_\n )\n {\n if (mainSource_ < 1 || mainSource_ > 3) {\n revert FluidOracleError(ErrorTypes.FallbackOracle__InvalidParams);\n }\n _FALLBACK_ORACLE2_MAIN_SOURCE = mainSource_;\n }\n\n /// @dev returns the exchange rate for the main oracle source, or the fallback source (if configured) if the main exchange rate\n /// fails to be fetched. If returned rate is 0, fetching rate failed or something went wrong.\n /// @return exchangeRate_ exchange rate\n /// @return fallback_ whether fallback was necessary or not\n function _getRateWithFallback2() internal view returns (uint256 exchangeRate_, bool fallback_) {\n if (_FALLBACK_ORACLE2_MAIN_SOURCE == 1) {\n // 1 = Chainlink ONLY (no fallback)\n exchangeRate_ = _getChainlinkExchangeRate2();\n } else if (_FALLBACK_ORACLE2_MAIN_SOURCE == 2) {\n // 2 = Chainlink with Redstone Fallback\n exchangeRate_ = _getChainlinkExchangeRate2();\n if (exchangeRate_ == 0) {\n fallback_ = true;\n exchangeRate_ = _getRedstoneExchangeRate2();\n }\n } else {\n // 3 = Redstone with Chainlink Fallback\n exchangeRate_ = _getRedstoneExchangeRate2();\n if (exchangeRate_ == 0) {\n fallback_ = true;\n exchangeRate_ = _getChainlinkExchangeRate2();\n }\n }\n }\n\n /// @dev returns the exchange rate for Chainlink, or Redstone if configured & Chainlink fails.\n function _getChainlinkOrRedstoneAsFallback2() internal view returns (uint256 exchangeRate_) {\n exchangeRate_ = _getChainlinkExchangeRate2();\n\n if (exchangeRate_ == 0 && _FALLBACK_ORACLE2_MAIN_SOURCE != 1) {\n // Chainlink failed but Redstone is configured too -> try Redstone\n exchangeRate_ = _getRedstoneExchangeRate2();\n }\n }\n}\n" }, "contracts/oracle/implementations/redstoneOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { RedstoneStructs } from \"./structs.sol\";\n\n/// @title Redstone Oracle implementation\n/// @notice This contract is used to get the exchange rate from a Redstone Oracle\nabstract contract RedstoneOracleImpl is OracleError, RedstoneStructs {\n /// @notice Redstone price oracle to check for the exchange rate\n IRedstoneOracle internal immutable _REDSTONE_ORACLE;\n /// @notice Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _REDSTONE_INVERT_RATE;\n\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _REDSTONE_PRICE_SCALER_MULTIPLIER;\n /// @notice constant value for inverting price to reduce gas usage\n uint256 internal immutable _REDSTONE_INVERT_PRICE_DIVIDEND;\n\n address internal immutable _REDSTONE_ORACLE_NOT_SET_ADDRESS = 0x000000000000000000000000000000000000dEaD;\n\n /// @notice constructor sets the Redstone oracle data\n constructor(RedstoneOracleData memory oracleData_) {\n if (address(oracleData_.oracle) == address(0) || oracleData_.token0Decimals == 0) {\n revert FluidOracleError(ErrorTypes.RedstoneOracle__InvalidParams);\n }\n\n _REDSTONE_ORACLE = oracleData_.oracle;\n _REDSTONE_INVERT_RATE = oracleData_.invertRate;\n\n // for explanation on how to get to scaler multiplier and dividend see `chainlinkOracleImpl.sol`.\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _REDSTONE_PRICE_SCALER_MULTIPLIER = address(oracleData_.oracle) == _REDSTONE_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - oracleData_.token0Decimals);\n _REDSTONE_INVERT_PRICE_DIVIDEND = address(oracleData_.oracle) == _REDSTONE_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + oracleData_.token0Decimals);\n }\n\n /// @dev Get the exchange rate from Redstone oracle\n /// @param rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getRedstoneExchangeRate() internal view returns (uint256 rate_) {\n try _REDSTONE_ORACLE.getExchangeRate() returns (uint256 exchangeRate_) {\n if (_REDSTONE_INVERT_RATE) {\n // invert the price\n return _REDSTONE_INVERT_PRICE_DIVIDEND / exchangeRate_;\n } else {\n return exchangeRate_ * _REDSTONE_PRICE_SCALER_MULTIPLIER;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Redstone oracle related data as utility for easy off-chain use / block explorer in a single view method\n function redstoneOracleData()\n public\n view\n returns (uint256 redstoneExchangeRate_, IRedstoneOracle redstoneOracle_, bool redstoneInvertRate_)\n {\n return (\n address(_REDSTONE_ORACLE) == _REDSTONE_ORACLE_NOT_SET_ADDRESS ? 0 : _getRedstoneExchangeRate(),\n _REDSTONE_ORACLE,\n _REDSTONE_INVERT_RATE\n );\n }\n}\n" }, "contracts/oracle/implementations/redstoneOracleImpl2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { RedstoneStructs } from \"./structs.sol\";\n\n// @dev Exact same contract as RedstoneOracleImpl, just with all vars, immutables etc. renamed with a \"2\" to avoid\n// conflicts when RedstoneOracleImpl would have to be inherited twice.\n\n/// @title Redstone Oracle implementation\n/// @notice This contract is used to get the exchange rate from a Redstone Oracle\nabstract contract RedstoneOracleImpl2 is OracleError, RedstoneStructs {\n /// @notice Redstone price oracle to check for the exchange rate\n IRedstoneOracle internal immutable _REDSTONE2_ORACLE;\n /// @notice Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _REDSTONE2_INVERT_RATE;\n\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _REDSTONE2_PRICE_SCALER_MULTIPLIER;\n /// @notice constant value for inverting price to reduce gas usage\n uint256 internal immutable _REDSTONE2_INVERT_PRICE_DIVIDEND;\n\n address internal immutable _REDSTONE2_ORACLE_NOT_SET_ADDRESS = 0x000000000000000000000000000000000000dEaD;\n\n /// @notice constructor sets the Redstone oracle data\n constructor(RedstoneOracleData memory oracleData_) {\n if (address(oracleData_.oracle) == address(0) || oracleData_.token0Decimals == 0) {\n revert FluidOracleError(ErrorTypes.RedstoneOracle__InvalidParams);\n }\n\n _REDSTONE2_ORACLE = oracleData_.oracle;\n _REDSTONE2_INVERT_RATE = oracleData_.invertRate;\n\n // for explanation on how to get to scaler multiplier and dividend see `chainlinkOracleImpl.sol`.\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _REDSTONE2_PRICE_SCALER_MULTIPLIER = address(oracleData_.oracle) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - oracleData_.token0Decimals);\n _REDSTONE2_INVERT_PRICE_DIVIDEND = address(oracleData_.oracle) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + oracleData_.token0Decimals);\n }\n\n /// @dev Get the exchange rate from Redstone oracle\n /// @param rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getRedstoneExchangeRate2() internal view returns (uint256 rate_) {\n try _REDSTONE2_ORACLE.getExchangeRate() returns (uint256 exchangeRate_) {\n if (_REDSTONE2_INVERT_RATE) {\n // invert the price\n return _REDSTONE2_INVERT_PRICE_DIVIDEND / exchangeRate_;\n } else {\n return exchangeRate_ * _REDSTONE2_PRICE_SCALER_MULTIPLIER;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Redstone oracle related data as utility for easy off-chain use / block explorer in a single view method\n function redstoneOracleData2()\n public\n view\n returns (uint256 redstoneExchangeRate_, IRedstoneOracle redstoneOracle_, bool redstoneInvertRate_)\n {\n return (\n address(_REDSTONE2_ORACLE) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS ? 0 : _getRedstoneExchangeRate2(),\n _REDSTONE2_ORACLE,\n _REDSTONE2_INVERT_RATE\n );\n }\n}\n" }, "contracts/oracle/implementations/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IChainlinkAggregatorV3 } from \"../interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\n\nabstract contract ChainlinkStructs {\n struct ChainlinkFeedData {\n /// @param feed address of Chainlink feed.\n IChainlinkAggregatorV3 feed;\n /// @param invertRate true if rate read from price feed must be inverted.\n bool invertRate;\n /// @param token0Decimals decimals of asset 0. E.g. for a USDC/ETH feed, USDC is token0 and has 6 decimals.\n /// (token1Decimals are available directly via Chainlink `FEED.decimals()`)\n uint256 token0Decimals;\n }\n\n struct ChainlinkConstructorParams {\n /// @param param hops count of hops, used for sanity checks. Must be 1, 2 or 3.\n uint8 hops;\n /// @param feed1 Chainlink feed 1 data. Required.\n ChainlinkFeedData feed1;\n /// @param feed2 Chainlink feed 2 data. Required if hops > 1.\n ChainlinkFeedData feed2;\n /// @param feed3 Chainlink feed 3 data. Required if hops > 2.\n ChainlinkFeedData feed3;\n }\n}\n\nabstract contract RedstoneStructs {\n struct RedstoneOracleData {\n /// @param oracle address of Redstone oracle.\n IRedstoneOracle oracle;\n /// @param invertRate true if rate read from price feed must be inverted.\n bool invertRate;\n /// @param token0Decimals decimals of asset 0. E.g. for a USDC/ETH feed, USDC is token0 and has 6 decimals.\n /// (token1Decimals are available directly via Redstone `Oracle.decimals()`)\n uint256 token0Decimals;\n }\n}\n" }, "contracts/oracle/implementations/sUSDeOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title sUSDe Oracle Implementation\n/// @notice This contract is used to get the exchange rate between sUSDe and USDe, adjusted for token decimals\n/// of a debt token (e.g. USDC / USDT)\nabstract contract SUSDeOracleImpl is OracleError {\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _SUSDE_PRICE_SCALER_MULTIPLIER;\n\n /// @notice SUSDE contract, e.g. on mainnet 0x9d39a5de30e57443bff2a8307a4256c8797a3497\n IERC4626 internal immutable _SUSDE;\n\n uint8 internal immutable _DEBT_TOKEN_DECIMALS;\n\n /// @notice constructor sets the sUSDe `sUSDe_` token address.\n constructor(IERC4626 sUSDe_, uint8 debtTokenDecimals_) {\n if (address(sUSDe_) == address(0) || debtTokenDecimals_ < 6) {\n revert FluidOracleError(ErrorTypes.SUSDeOracle__InvalidParams);\n }\n\n _SUSDE = sUSDe_;\n\n // debt token decimals is used to make sure the returned exchange rate is scaled correctly e.g.\n // for an exchange rate between sUSDe and USDC (this Oracle returning amount of USDC for 1e18 sUSDe).\n _DEBT_TOKEN_DECIMALS = debtTokenDecimals_;\n\n _SUSDE_PRICE_SCALER_MULTIPLIER = 10 ** (debtTokenDecimals_ - 6);\n // e.g. when:\n // - debtTokenDecimals_ = 6 -> scaler multiplier is 1\n // - debtTokenDecimals_ = 7 -> scaler multiplier is 10\n // - debtTokenDecimals_ = 18 -> scaler multiplier is 1e12\n // -> gets 1e15 returned exchange rate to 1e27\n }\n\n /// @notice Get the exchange rate from sUSDe contract (amount of USDe for 1 sUSDe)\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getSUSDeExchangeRate() internal view returns (uint256 rate_) {\n return _SUSDE.convertToAssets(1e15) * _SUSDE_PRICE_SCALER_MULTIPLIER;\n }\n\n /// @notice returns all sUSDe oracle related data as utility for easy off-chain use / block explorer in a single view method\n function sUSDeOracleData()\n public\n view\n returns (uint256 sUSDeExchangeRate_, IERC4626 sUSDe_, uint256 debtTokenDecimals_)\n {\n return (_getSUSDeExchangeRate(), _SUSDE, _DEBT_TOKEN_DECIMALS);\n }\n}\n" }, "contracts/oracle/implementations/uniV3OracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { FullMath } from \"../libraries/FullMath.sol\";\nimport { TickMath } from \"../libraries/TickMath.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { IUniswapV3Pool } from \"../interfaces/external/IUniswapV3Pool.sol\";\nimport { Error as OracleError } from \"../error.sol\";\n\n/// @title Uniswap V3 Oracle implementation\n/// @notice This contract is used to get the exchange rate from from a Uniswap V3 Pool,\n/// including logic to check against TWAP max deltas.\n/// @dev Uses 5 secondsAgos[] values and 3 TWAP maxDeltas:\n/// e.g. [240, 60, 15, 1, 0] -> [price240to60, price60to15, price 15to1, currentPrice]\n/// delta checks: price240to60 vs currentPrice, price60to15 vs currentPrice and 15to1 vs currentPrice.\nabstract contract UniV3OracleImpl is OracleError {\n /// @dev Uniswap V3 Pool to check for the exchange rate\n IUniswapV3Pool internal immutable _POOL;\n\n /// @dev Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _UNIV3_INVERT_RATE;\n\n /// @dev Uniswap oracle delta for TWAP1 in 1e2 percent. If uniswap price TWAP1 is out of this delta,\n /// current price fetching reverts. E.g. for delta of TWAP 240 -> 60 vs current price\n uint256 internal immutable _UNI_TWAP1_MAX_DELTA_PERCENT;\n /// @dev Uniswap oracle delta for TWAP2 in 1e2 percent. If uniswap price TWAP2 is out of this delta,\n /// current price fetching reverts. E.g. for delta of TWAP 60 -> 15 vs current price\n uint256 internal immutable _UNI_TWAP2_MAX_DELTA_PERCENT;\n /// @dev Uniswap oracle delta for TWAP3 in 1e2 percent. If uniswap price TWAP3 is out of this delta,\n /// current price fetching reverts. E.g. for delta of TWAP 15 -> 1 vs current price\n uint256 internal immutable _UNI_TWAP3_MAX_DELTA_PERCENT;\n\n /// @dev Uniswap oracle seconds ago for twap, 1. value, e.g. 240\n uint256 internal immutable _UNI_SECONDS_AGO_1;\n /// @dev Uniswap oracle seconds ago for twap, 2. value, e.g. 60\n uint256 internal immutable _UNI_SECONDS_AGO_2;\n /// @dev Uniswap oracle seconds ago for twap, 3. value, e.g. 15\n uint256 internal immutable _UNI_SECONDS_AGO_3;\n /// @dev Uniswap oracle seconds ago for twap, 4. value, e.g. 1\n uint256 internal immutable _UNI_SECONDS_AGO_4;\n /// @dev Uniswap oracle seconds ago for twap, 5. value, e.g. 0\n uint256 internal immutable _UNI_SECONDS_AGO_5;\n\n /// @dev Uniswap TWAP1 interval duration.\n int256 internal immutable _UNI_TWAP1_INTERVAL;\n /// @dev Uniswap TWAP2 interval duration.\n int256 internal immutable _UNI_TWAP2_INTERVAL;\n /// @dev Uniswap TWAP3 interval duration.\n int256 internal immutable _UNI_TWAP3_INTERVAL;\n /// @dev Uniswap TWAP4 interval duration.\n int256 internal immutable _UNI_TWAP4_INTERVAL;\n\n /// @dev stored array lengths to optimize gas\n uint256 internal constant _SECONDS_AGOS_LENGTH = 5;\n uint256 internal constant _TWAP_DELTAS_LENGTH = 3;\n\n /// @dev constant value for price scaling to reduce gas usage\n uint256 internal immutable _UNIV3_PRICE_SCALER_MULTIPLIER;\n /// @dev constant value for inverting price to reduce gas usage\n uint256 internal immutable _UNIV3_INVERT_PRICE_DIVIDEND;\n\n struct UniV3ConstructorParams {\n /// @param pool Uniswap V3 Pool to check for the exchange rate\n IUniswapV3Pool pool;\n /// @param invertRate Flag to invert the Uniswap price or not\n bool invertRate;\n /// @param tWAPMaxDeltaPercents Uniswap oracle delta for TWAP1-2-3 in 1e2 percent\n uint256[_TWAP_DELTAS_LENGTH] tWAPMaxDeltaPercents;\n /// @param secondsAgos Uniswap oracle seconds ago for the 3 TWAP values, from oldest to newest, e.g. [240, 60, 15, 1, 0]\n uint32[_SECONDS_AGOS_LENGTH] secondsAgos;\n }\n\n /// @notice constructor sets the Uniswap V3 `pool_` to check for the exchange rate and the `invertRate_` flag.\n /// E.g. `invertRate_` should be true if for the WETH/USDC pool it's expected that the oracle returns USDC per 1 WETH\n constructor(UniV3ConstructorParams memory params_) {\n if (address(params_.pool) == address(0)) {\n revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidParams);\n }\n // sanity check that seconds agos values are ordered ascending, e.g. [240, 60, 15, 1, 0]\n if (\n params_.secondsAgos[0] <= params_.secondsAgos[1] ||\n params_.secondsAgos[1] <= params_.secondsAgos[2] ||\n params_.secondsAgos[2] <= params_.secondsAgos[3] ||\n params_.secondsAgos[3] <= params_.secondsAgos[4]\n ) {\n revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidSecondsAgos);\n }\n // sanity check that deltas are less than 100% and decreasing (as timespan is closer to current price):\n // 1. delta must < 100%\n // all following deltas must be <= than the previous one\n if (\n params_.tWAPMaxDeltaPercents[0] >= OracleUtils.HUNDRED_PERCENT_DELTA_SCALER ||\n params_.tWAPMaxDeltaPercents[1] > params_.tWAPMaxDeltaPercents[0] ||\n params_.tWAPMaxDeltaPercents[2] > params_.tWAPMaxDeltaPercents[1]\n ) {\n revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidDeltas);\n }\n\n _UNI_SECONDS_AGO_1 = uint256(params_.secondsAgos[0]);\n _UNI_SECONDS_AGO_2 = uint256(params_.secondsAgos[1]);\n _UNI_SECONDS_AGO_3 = uint256(params_.secondsAgos[2]);\n _UNI_SECONDS_AGO_4 = uint256(params_.secondsAgos[3]);\n _UNI_SECONDS_AGO_5 = uint256(params_.secondsAgos[4]);\n\n _UNI_TWAP1_INTERVAL = int256(uint256(params_.secondsAgos[0] - params_.secondsAgos[1]));\n _UNI_TWAP2_INTERVAL = int256(uint256(params_.secondsAgos[1] - params_.secondsAgos[2]));\n _UNI_TWAP3_INTERVAL = int256(uint256(params_.secondsAgos[2] - params_.secondsAgos[3]));\n _UNI_TWAP4_INTERVAL = int256(uint256(params_.secondsAgos[3] - params_.secondsAgos[4]));\n\n _UNI_TWAP1_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[0]; // e.g. for TWAP 240 -> 60 vs current price\n _UNI_TWAP2_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[1]; // e.g. for TWAP 60 -> 15 vs current price\n _UNI_TWAP3_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[2]; // e.g. for TWAP 15 -> 1 vs current price\n\n _POOL = params_.pool;\n _UNIV3_INVERT_RATE = params_.invertRate;\n\n // uniswapV3 returned price is already scaled to token decimals.\n _UNIV3_PRICE_SCALER_MULTIPLIER = 10 ** OracleUtils.RATE_OUTPUT_DECIMALS;\n // uniV3 invert price dividend happens on the already scaled by 1e27 result for price in token1 per 1 token0\n _UNIV3_INVERT_PRICE_DIVIDEND = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS * 2);\n }\n\n /// @dev Get the last exchange rate from the pool's last observed value without any checks\n /// @return exchangeRateUnsafe_ The exchange rate between the underlying asset and the peg asset in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getUniV3ExchangeRateUnsafe() internal view returns (uint256 exchangeRateUnsafe_) {\n (uint160 sqrtPriceX96_, , , , , , ) = _POOL.slot0();\n\n exchangeRateUnsafe_ = _UNIV3_INVERT_RATE\n ? _invertUniV3Price(_getPriceFromSqrtPriceX96(sqrtPriceX96_))\n : _getPriceFromSqrtPriceX96(sqrtPriceX96_);\n }\n\n /// @dev Get the last exchange rate from the pool's last observed value, checked against TWAP deviations.\n /// @return exchangeRate_ The exchange rate between the underlying asset and the peg asset in `OracleUtils.RATE_OUTPUT_DECIMALS`\n /// If 0 then the fetching the price failed or a delta was invalid.\n function _getUniV3ExchangeRate() internal view returns (uint256 exchangeRate_) {\n // build calldata bytes in a gas-optimized way without having to build an array / using abi.encode.\n // gas efficient work around for Solidity not supporting immutable non-value types.\n bytes memory data_ = abi.encodePacked(\n hex\"883bdbfd\", // pack function selector\n hex\"0000000000000000000000000000000000000000000000000000000000000020\", // pack start offset of dynamic array\n _SECONDS_AGOS_LENGTH, // pack length of dynamic array\n // pack seconds agos values:\n _UNI_SECONDS_AGO_1,\n _UNI_SECONDS_AGO_2,\n _UNI_SECONDS_AGO_3,\n _UNI_SECONDS_AGO_4,\n _UNI_SECONDS_AGO_5\n );\n\n // get the tickCumulatives from Pool.observe()\n (bool success_, bytes memory result_) = address(_POOL).staticcall(data_);\n\n if (!success_) {\n return 0;\n }\n int56[] memory tickCumulatives_ = abi.decode(result_, (int56[]));\n\n unchecked {\n int24 exchangeRateTick_;\n {\n int56 tickCumulativesDelta_ = (tickCumulatives_[_TWAP_DELTAS_LENGTH + 1] -\n tickCumulatives_[_TWAP_DELTAS_LENGTH]);\n // _UNI_TWAP4_INTERVAL can not be 0 because of constructor sanity checks\n exchangeRateTick_ = int24(tickCumulativesDelta_ / _UNI_TWAP4_INTERVAL);\n // Always round to negative infinity, see UniV3 OracleLibrary\n // https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/libraries/OracleLibrary.sol#L36\n if (tickCumulativesDelta_ < 0 && (tickCumulativesDelta_ % _UNI_TWAP4_INTERVAL != 0)) {\n exchangeRateTick_--;\n }\n }\n\n // Check the latest Uniswap price is within the acceptable delta from each TWAP range\n // TWAP 1 check\n if (\n _isInvalidTWAPDelta(\n int256(exchangeRateTick_),\n tickCumulatives_[1] - tickCumulatives_[0],\n _UNI_TWAP1_INTERVAL,\n int256(_UNI_TWAP1_MAX_DELTA_PERCENT)\n )\n ) {\n return 0;\n }\n\n // TWAP 2 check\n if (\n _isInvalidTWAPDelta(\n int256(exchangeRateTick_),\n tickCumulatives_[2] - tickCumulatives_[1],\n _UNI_TWAP2_INTERVAL,\n int256(_UNI_TWAP2_MAX_DELTA_PERCENT)\n )\n ) {\n return 0;\n }\n\n // TWAP 3 check\n if (\n _isInvalidTWAPDelta(\n int256(exchangeRateTick_),\n tickCumulatives_[3] - tickCumulatives_[2],\n _UNI_TWAP3_INTERVAL,\n int256(_UNI_TWAP3_MAX_DELTA_PERCENT)\n )\n ) {\n return 0;\n }\n\n // get the current uniswap price, which is the last tick cumulatives interval, usually [..., 1, 0]\n exchangeRate_ = _getPriceFromSqrtPriceX96(TickMath.getSqrtRatioAtTick(exchangeRateTick_));\n if (_UNIV3_INVERT_RATE) {\n exchangeRate_ = _invertUniV3Price(exchangeRate_);\n }\n }\n }\n\n /// @dev verifies that `exchangeRate_` is within `maxDelta_` for derived price from `tickCumulativesDelta_` and `interval_`.\n /// returns true if delta is invalid\n function _isInvalidTWAPDelta(\n int256 exchangeRateTick_,\n int256 tickCumulativesDelta_,\n int256 interval_, // can not be 0 because of constructor sanity checks\n int256 maxDelta_\n ) internal pure returns (bool) {\n unchecked {\n int256 arithmeticMeanTick_ = int256(tickCumulativesDelta_ / interval_);\n // Always round to negative infinity, see UniV3 OracleLibrary\n // https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/libraries/OracleLibrary.sol#L36\n if (tickCumulativesDelta_ < 0 && (tickCumulativesDelta_ % interval_ != 0)) {\n arithmeticMeanTick_--;\n }\n\n // Check that the uniswapPrice is within DELTA of the Uniswap TWAP (via tick)\n // each univ3 tick is 0.01% increase or decrease in price. `maxDelta_` has near to same precision.\n // Note: near to the same because each Uniswap tick is 0.01% away so price of ticks are if current one is 100 then next will be:\n // 100 + 100 * 0.01% = 100.01\n // 100.01 + 100.01 * 0.01% = 100.020001\n if (\n exchangeRateTick_ > (arithmeticMeanTick_ + maxDelta_) ||\n exchangeRateTick_ < (arithmeticMeanTick_ - maxDelta_)\n ) {\n // Uniswap last price is NOT within the delta\n return true;\n }\n }\n return false;\n }\n\n /// @notice returns all UniV3 oracle related data as utility for easy off-chain use / block explorer in a single view method\n function uniV3OracleData()\n public\n view\n returns (\n IUniswapV3Pool uniV3Pool_,\n bool uniV3InvertRate_,\n uint32[] memory uniV3secondsAgos_,\n uint256[] memory uniV3TwapDeltas_,\n uint256 uniV3exchangeRateUnsafe_,\n uint256 uniV3exchangeRate_\n )\n {\n // Get the latest TWAP prices from the Uniswap Oracle for second intervals\n uniV3secondsAgos_ = new uint32[](_SECONDS_AGOS_LENGTH);\n uniV3secondsAgos_[0] = uint32(_UNI_SECONDS_AGO_1);\n uniV3secondsAgos_[1] = uint32(_UNI_SECONDS_AGO_2);\n uniV3secondsAgos_[2] = uint32(_UNI_SECONDS_AGO_3);\n uniV3secondsAgos_[3] = uint32(_UNI_SECONDS_AGO_4);\n uniV3secondsAgos_[4] = uint32(_UNI_SECONDS_AGO_5);\n\n // Check the latest Uniswap price is within the acceptable delta from each TWAP range\n uniV3TwapDeltas_ = new uint256[](_TWAP_DELTAS_LENGTH);\n uniV3TwapDeltas_[0] = _UNI_TWAP1_MAX_DELTA_PERCENT;\n uniV3TwapDeltas_[1] = _UNI_TWAP2_MAX_DELTA_PERCENT;\n uniV3TwapDeltas_[2] = _UNI_TWAP3_MAX_DELTA_PERCENT;\n\n return (\n _POOL,\n _UNIV3_INVERT_RATE,\n uniV3secondsAgos_,\n uniV3TwapDeltas_,\n _getUniV3ExchangeRateUnsafe(),\n _getUniV3ExchangeRate()\n );\n }\n\n /// @dev Get the price from the sqrt price in `OracleUtils.RATE_OUTPUT_DECIMALS`\n /// (see https://blog.uniswap.org/uniswap-v3-math-primer)\n /// @param sqrtPriceX96_ The sqrt price to convert\n function _getPriceFromSqrtPriceX96(uint160 sqrtPriceX96_) private view returns (uint256 priceX96_) {\n return\n FullMath.mulDiv(\n uint256(sqrtPriceX96_) * uint256(sqrtPriceX96_),\n _UNIV3_PRICE_SCALER_MULTIPLIER,\n 1 << 192 // 2^96 * 2\n );\n }\n\n /// @dev Invert the price\n /// @param price_ The price to invert\n /// @return invertedPrice_ The inverted price in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _invertUniV3Price(uint256 price_) private view returns (uint256 invertedPrice_) {\n return _UNIV3_INVERT_PRICE_DIVIDEND / price_;\n }\n}\n" }, "contracts/oracle/implementations/weETHOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title weETH Oracle Implementation\n/// @notice This contract is used to get the exchange rate between weETH and eETH\nabstract contract WeETHOracleImpl is OracleError {\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _WEETH_PRICE_SCALER_MULTIPLIER;\n\n /// @notice WEETH contract, e.g. on mainnet 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee\n IWeETH internal immutable _WEETH;\n\n /// @notice constructor sets the weETH (Etherfi's wrapped eETH) `weETH_` token address.\n constructor(IWeETH weETH_) {\n if (address(weETH_) == address(0)) {\n revert FluidOracleError(ErrorTypes.WeETHOracle__InvalidParams);\n }\n\n _WEETH = weETH_;\n\n _WEETH_PRICE_SCALER_MULTIPLIER = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - 18); // e.g. 1e9\n }\n\n /// @notice Get the exchange rate from weETH contract\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getWeETHExchangeRate() internal view returns (uint256 rate_) {\n return _WEETH.getEETHByWeETH(1e18) * _WEETH_PRICE_SCALER_MULTIPLIER;\n }\n\n /// @notice returns all weETH oracle related data as utility for easy off-chain use / block explorer in a single view method\n function weETHOracleData() public view returns (uint256 weETHExchangeRate_, IWeETH weETH_) {\n return (_getWeETHExchangeRate(), _WEETH);\n }\n}\n" }, "contracts/oracle/implementations/wstETHOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title wstETH Oracle Implementation\n/// @notice This contract is used to get the exchange rate between wstETH and stETH\nabstract contract WstETHOracleImpl is OracleError {\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _WSTETH_PRICE_SCALER_MULTIPLIER;\n\n /// @notice WSTETH contract, e.g. on mainnet 0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0\n IWstETH internal immutable _WSTETH;\n\n /// @notice constructor sets the wstETH `wstETH_` token address.\n constructor(IWstETH wstETH_) {\n if (address(wstETH_) == address(0)) {\n revert FluidOracleError(ErrorTypes.WstETHOracle__InvalidParams);\n }\n\n _WSTETH = wstETH_;\n\n _WSTETH_PRICE_SCALER_MULTIPLIER = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - 18); // e.g. 1e9\n }\n\n /// @notice Get the exchange rate from wstETH contract\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getWstETHExchangeRate() internal view returns (uint256 rate_) {\n return _WSTETH.stEthPerToken() * _WSTETH_PRICE_SCALER_MULTIPLIER;\n }\n\n /// @notice returns all wWtETH oracle related data as utility for easy off-chain use / block explorer in a single view method\n function wstETHOracleData() public view returns (uint256 wstETHExchangeRate_, IWstETH wstETH_) {\n return (_getWstETHExchangeRate(), _WSTETH);\n }\n}\n" }, "contracts/oracle/interfaces/external/IChainlinkAggregatorV3.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\n/// from https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\n/// Copyright (c) 2018 SmartContract ChainLink, Ltd.\n\ninterface IChainlinkAggregatorV3 {\n /// @notice represents the number of decimals the aggregator responses represent.\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n function getRoundData(\n uint80 _roundId\n )\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n\n function latestRoundData()\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n}\n" }, "contracts/oracle/interfaces/external/IRedstoneOracle.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IRedstoneOracle {\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset\n // @dev custom Redstone adapter for Instadapp implementation\n function getExchangeRate() external view returns (uint256 exchangeRate_);\n\n /**\n * @notice Returns the number of decimals for the price feed\n * @dev By default, RedStone uses 8 decimals for data feeds\n * @return decimals The number of decimals in the price feed values\n */\n // see https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol#L51C12-L51C20\n function decimals() external view returns (uint8);\n}\n" }, "contracts/oracle/interfaces/external/IUniswapV3Pool.sol": { "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.21;\n\n/// from https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces.\n/// Copyright (c) 2022 Uniswap Labs\n\n/// @title Pool state that never changes\n/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values\ninterface IUniswapV3PoolImmutables {\n /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface\n /// @return The contract address\n function factory() external view returns (address);\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6\n /// @return The fee\n function fee() external view returns (uint24);\n\n /// @notice The pool tick spacing\n /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive\n /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...\n /// This value is an int24 to avoid casting even though it is always positive.\n /// @return The tick spacing\n function tickSpacing() external view returns (int24);\n\n /// @notice The maximum amount of position liquidity that can use any tick in the range\n /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and\n /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool\n /// @return The max amount of liquidity per tick\n function maxLiquidityPerTick() external view returns (uint128);\n}\n\n/// @title Pool state that is not stored\n/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the\n/// blockchain. The functions here may have variable gas costs.\ninterface IUniswapV3PoolDerivedState {\n /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp\n /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing\n /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,\n /// you must call it with secondsAgos = [3600, 0].\n /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in\n /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.\n /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned\n /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp\n /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block\n /// timestamp\n function observe(\n uint32[] calldata secondsAgos\n ) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);\n\n /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range\n /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.\n /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first\n /// snapshot is taken and the second snapshot is taken.\n /// @param tickLower The lower tick of the range\n /// @param tickUpper The upper tick of the range\n /// @return tickCumulativeInside The snapshot of the tick accumulator for the range\n /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range\n /// @return secondsInside The snapshot of seconds per liquidity for the range\n function snapshotCumulativesInside(\n int24 tickLower,\n int24 tickUpper\n ) external view returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);\n}\n\n/// @title Pool state that can change\n/// @notice These methods compose the pool's state, and can change with any frequency including multiple times\n/// per transaction\ninterface IUniswapV3PoolState {\n /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas\n /// when accessed externally.\n /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value\n /// tick The current tick of the pool, i.e. according to the last tick transition that was run.\n /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick\n /// boundary.\n /// observationIndex The index of the last oracle observation that was written,\n /// observationCardinality The current maximum number of observations stored in the pool,\n /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.\n /// feeProtocol The protocol fee for both tokens of the pool.\n /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0\n /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.\n /// unlocked Whether the pool is currently locked to reentrancy\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n uint8 feeProtocol,\n bool unlocked\n );\n\n /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool\n /// @dev This value can overflow the uint256\n function feeGrowthGlobal0X128() external view returns (uint256);\n\n /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool\n /// @dev This value can overflow the uint256\n function feeGrowthGlobal1X128() external view returns (uint256);\n\n /// @notice The amounts of token0 and token1 that are owed to the protocol\n /// @dev Protocol fees will never exceed uint128 max in either token\n function protocolFees() external view returns (uint128 token0, uint128 token1);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(\n int24 tick\n )\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n\n /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information\n function tickBitmap(int16 wordPosition) external view returns (uint256);\n\n /// @notice Returns the information about a position by the position's key\n /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper\n /// @return _liquidity The amount of liquidity in the position,\n /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,\n /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,\n /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,\n /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke\n function positions(\n bytes32 key\n )\n external\n view\n returns (\n uint128 _liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n /// @notice Returns data about a specific observation index\n /// @param index The element of the observations array to fetch\n /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time\n /// ago, rather than at a specific index in the array.\n /// @return blockTimestamp The timestamp of the observation,\n /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,\n /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,\n /// Returns initialized whether the observation has been initialized and the values are safe to use\n function observations(\n uint256 index\n )\n external\n view\n returns (\n uint32 blockTimestamp,\n int56 tickCumulative,\n uint160 secondsPerLiquidityCumulativeX128,\n bool initialized\n );\n}\n\n/// @title The interface for a Uniswap V3 Pool\n/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface IUniswapV3Pool is IUniswapV3PoolImmutables, IUniswapV3PoolState, IUniswapV3PoolDerivedState {\n\n}\n" }, "contracts/oracle/interfaces/external/IWeETH.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IWeETH {\n /**\n * @notice Get amount of eETH for {_weETHAmount} weETH\n * @return Amount of eETH for {_weETHAmount} weETH\n */\n function getEETHByWeETH(uint256 _weETHAmount) external view returns (uint256);\n\n /**\n * @notice Get amount of weETH for {_eETHAmount} eETH\n * @return Amount of weETH for {_eETHAmount} eETH\n */\n function getWeETHByeETH(uint256 _eETHAmount) external view returns (uint256);\n}\n" }, "contracts/oracle/interfaces/external/IWstETH.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IWstETH {\n /**\n * @notice Get amount of stETH for 1 wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for 1 stETH\n * @return Amount of wstETH for 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n}\n" }, "contracts/oracle/interfaces/iFluidOracle.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidOracle {\n /// @dev Deprecated. Use `getExchangeRateOperate()` and `getExchangeRateLiquidate()` instead. Only implemented for\n /// backwards compatibility.\n function getExchangeRate() external view returns (uint256 exchangeRate_);\n\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for operates\n function getExchangeRateOperate() external view returns (uint256 exchangeRate_);\n\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for liquidations\n function getExchangeRateLiquidate() external view returns (uint256 exchangeRate_);\n}\n" }, "contracts/oracle/libraries/FullMath.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\n/// @dev Modified from the original UniswapV3 library to support v0.8\n/// From: uint256 twos = -denominator & denominator;\n/// To: uint256 twos = (type(uint256).max - denominator + 1) & denominator;\n\n/// @title Contains 512-bit math functions\n/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision\n/// @dev Handles \"phantom overflow\" i.e., allows multiplication and division where an intermediate value overflows 256 bits\nlibrary FullMath {\n /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n /// @param a The multiplicand\n /// @param b The multiplier\n /// @param denominator The divisor\n /// @return result The 256-bit result\n /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv\n function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {\n // 512-bit multiply [prod1 prod0] = a * b\n // Compute the product mod 2**256 and mod 2**256 - 1\n // then use the Chinese Remainder Theorem to reconstruct\n // the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2**256 + prod0\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(a, b, not(0))\n prod0 := mul(a, b)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division\n if (prod1 == 0) {\n require(denominator > 0);\n assembly {\n result := div(prod0, denominator)\n }\n return result;\n }\n\n // Make sure the result is less than 2**256.\n // Also prevents denominator == 0\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0]\n // Compute remainder using mulmod\n uint256 remainder;\n assembly {\n remainder := mulmod(a, b, denominator)\n }\n // Subtract 256 bit number from 512 bit number\n assembly {\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator\n // Compute largest power of two divisor of denominator.\n // Always >= 1.\n /// @dev This line was modified for v0.8.x\n // uint256 twos = -denominator & denominator;\n uint256 twos = (type(uint256).max - denominator + 1) & denominator;\n // Divide denominator by power of two\n assembly {\n denominator := div(denominator, twos)\n }\n\n // Divide [prod1 prod0] by the factors of two\n assembly {\n prod0 := div(prod0, twos)\n }\n // Shift in bits from prod1 into prod0. For this we need\n // to flip `twos` such that it is 2**256 / twos.\n // If twos is zero, then it becomes one\n assembly {\n twos := add(div(sub(0, twos), twos), 1)\n }\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2**256\n // Now that denominator is an odd number, it has an inverse\n // modulo 2**256 such that denominator * inv = 1 mod 2**256.\n // Compute the inverse by starting with a seed that is correct\n // correct for four bits. That is, denominator * inv = 1 mod 2**4\n uint256 inv = (3 * denominator) ^ 2;\n // Now use Newton-Raphson iteration to improve the precision.\n // Thanks to Hensel's lifting lemma, this also works in modular\n // arithmetic, doubling the correct bits in each step.\n inv *= 2 - denominator * inv; // inverse mod 2**8\n inv *= 2 - denominator * inv; // inverse mod 2**16\n inv *= 2 - denominator * inv; // inverse mod 2**32\n inv *= 2 - denominator * inv; // inverse mod 2**64\n inv *= 2 - denominator * inv; // inverse mod 2**128\n inv *= 2 - denominator * inv; // inverse mod 2**256\n\n // Because the division is now exact we can divide by multiplying\n // with the modular inverse of denominator. This will give us the\n // correct result modulo 2**256. Since the precoditions guarantee\n // that the outcome is less than 2**256, this is the final result.\n // We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inv;\n return result;\n }\n\n /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n /// @param a The multiplicand\n /// @param b The multiplier\n /// @param denominator The divisor\n /// @return result The 256-bit result\n function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {\n result = mulDiv(a, b, denominator);\n if (mulmod(a, b, denominator) > 0) {\n require(result < type(uint256).max);\n result++;\n }\n }\n}\n" }, "contracts/oracle/libraries/oracleUtils.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title Oracle utils library\n/// @notice implements common utility methods for Fluid Oracles\nlibrary OracleUtils {\n /// @dev The scaler for max delta point math (100%)\n uint256 internal constant HUNDRED_PERCENT_DELTA_SCALER = 10_000;\n /// @dev output precision of rates\n uint256 internal constant RATE_OUTPUT_DECIMALS = 27;\n\n /// @dev checks if `mainSourceRate_` is within a `maxDeltaPercent_` of `checkSourceRate_`. Returns true if so.\n function isRateOutsideDelta(\n uint256 mainSourceRate_,\n uint256 checkSourceRate_,\n uint256 maxDeltaPercent_\n ) internal pure returns (bool) {\n uint256 offset_ = (checkSourceRate_ * maxDeltaPercent_) / HUNDRED_PERCENT_DELTA_SCALER;\n return (mainSourceRate_ > (checkSourceRate_ + offset_) || mainSourceRate_ < (checkSourceRate_ - offset_));\n }\n}\n" }, "contracts/oracle/libraries/TickMath.sol": { "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.21;\n\n/// @dev Modified from the original UniswapV3 library to support v0.8\n/// From: require(absTick <= uint256(MAX_TICK), 'T');\n/// To: require(absTick <= uint256(int(MAX_TICK)), 'T');\n\n/// @title Math library for computing sqrt prices from ticks and vice versa\n/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports\n/// prices between 2**-128 and 2**128\nlibrary TickMath {\n /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128\n int24 internal constant MIN_TICK = -887272;\n /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128\n int24 internal constant MAX_TICK = -MIN_TICK;\n\n /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)\n uint160 internal constant MIN_SQRT_RATIO = 4295128739;\n /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)\n uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;\n\n /// @notice Calculates sqrt(1.0001^tick) * 2^96\n /// @dev Throws if |tick| > max tick\n /// @param tick The input tick for the above formula\n /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)\n /// at the given tick\n function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {\n uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n /// @dev This line was modified for v0.8.x\n // require(absTick <= uint256(MAX_TICK), 'T');\n require(absTick <= uint256(int(MAX_TICK)), \"T\");\n\n uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;\n if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;\n if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;\n if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;\n if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;\n if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;\n if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;\n if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;\n if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;\n if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;\n if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;\n if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;\n if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;\n if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;\n if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;\n if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;\n if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;\n if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;\n if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;\n if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;\n\n if (tick > 0) ratio = type(uint256).max / ratio;\n\n // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.\n // we then downcast because we know the result always fits within 160 bits due to our tick input constraint\n // we round up in the division so getTickAtSqrtRatio of the output price is always consistent\n sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));\n }\n\n /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio\n /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may\n /// ever return.\n /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96\n /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio\n function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {\n // second inequality must be < because the price can never reach the price at the max tick\n require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, \"R\");\n uint256 ratio = uint256(sqrtPriceX96) << 32;\n\n uint256 r = ratio;\n uint256 msb = 0;\n\n assembly {\n let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(5, gt(r, 0xFFFFFFFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(4, gt(r, 0xFFFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(3, gt(r, 0xFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(2, gt(r, 0xF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(1, gt(r, 0x3))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := gt(r, 0x1)\n msb := or(msb, f)\n }\n\n if (msb >= 128) r = ratio >> (msb - 127);\n else r = ratio << (127 - msb);\n\n int256 log_2 = (int256(msb) - 128) << 64;\n\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(63, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(62, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(61, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(60, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(59, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(58, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(57, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(56, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(55, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(54, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(53, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(52, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(51, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(50, f))\n }\n\n int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number\n\n int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);\n int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);\n\n tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;\n }\n}\n" }, "contracts/oracle/oracles/cLFallbackUniV3Oracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { ChainlinkOracleImpl } from \"../implementations/chainlinkOracleImpl.sol\";\nimport { UniV3OracleImpl } from \"../implementations/uniV3OracleImpl.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title Chainlink with Fallback to UniV3 Oracle\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a Chainlink price feed or, if that feed fails, the price from a UniV3 TWAP delta checked Oracle.\ncontract CLFallbackUniV3Oracle is FluidOracle, ChainlinkOracleImpl, UniV3OracleImpl {\n /// @notice sets the Chainlink and UniV3 Oracle configs.\n /// @param chainlinkParams_ ChainlinkOracle constructor params struct.\n /// @param uniV3Params_ UniV3Oracle constructor params struct.\n constructor(\n ChainlinkConstructorParams memory chainlinkParams_,\n UniV3ConstructorParams memory uniV3Params_\n ) ChainlinkOracleImpl(chainlinkParams_) UniV3OracleImpl(uniV3Params_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n exchangeRate_ = _getChainlinkExchangeRate();\n if (exchangeRate_ == 0) {\n // if Chainlink fails, use UniV3 rate (TWAP checked)\n exchangeRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n revert FluidOracleError(ErrorTypes.CLFallbackUniV3Oracle__ExchangeRateZero);\n }\n }\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n" }, "contracts/oracle/oracles/fallbackCLRSOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title Chainlink / Redstone Oracle (with fallback)\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a Chainlink price feed or a Redstone Oracle with one of them being used as main source and\n/// the other one acting as a fallback if the main source fails for any reason. Reverts if fetched rate is 0.\ncontract FallbackCLRSOracle is FluidOracle, FallbackOracleImpl {\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param mainSource_ which oracle to use as main source: 1 = Chainlink, 2 = Redstone (other one is fallback).\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n ) FallbackOracleImpl(mainSource_, chainlinkParams_, redstoneOracle_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n (exchangeRate_, ) = _getRateWithFallback();\n\n if (exchangeRate_ == 0) {\n revert FluidOracleError(ErrorTypes.FallbackCLRSOracle__ExchangeRateZero);\n }\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE_MAIN_SOURCE;\n }\n}\n" }, "contracts/oracle/oracles/sUSDeOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { SUSDeOracleImpl } from \"../implementations/sUSDeOracleImpl.sol\";\n\n/// @title SUSDeOracle\n/// @notice Gets the exchange rate between sUSDe and USDe directly from the sUSDe contract, adjusted for decimals\n/// of a debt token (get amount of debt token for 1 sUSDe).\ncontract SUSDeOracle is FluidOracle, SUSDeOracleImpl {\n /// @notice constructor sets the sUSDe `sUSDe_` token address and calculates scaling for exchange rate based on\n /// `debtTokenDecimals_` (token decimals of debt token, e.g. of USDC / USDT = 6)\n constructor(IERC4626 sUSDe_, uint8 debtTokenDecimals_) SUSDeOracleImpl(sUSDe_, debtTokenDecimals_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n}\n" }, "contracts/oracle/oracles/uniV3CheckCLRSOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { UniV3OracleImpl } from \"../implementations/uniV3OracleImpl.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title UniswapV3 checked against Chainlink / Redstone Oracle. Either one reported as exchange rate.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a UniV3 pool (compared against 3 TWAPs) and (optionally) comparing it against a Chainlink\n/// or Redstone price (one of Chainlink or Redstone being the main source and the other one the fallback source).\n/// Alternatively it can also use Chainlink / Redstone as main price and use UniV3 as check price.\n/// @dev The process for getting the aggregate oracle price is:\n/// 1. Fetch the UniV3 TWAPS, the latest interval is used as the current price\n/// 2. Verify this price is within an acceptable DELTA from the Uniswap TWAPS e.g.:\n/// a. 240 to 60s\n/// b. 60 to 15s\n/// c. 15 to 1s (last block)\n/// d. 1 to 0s (current)\n/// 3. (unless UniV3 only mode): Verify this price is within an acceptable DELTA from the Chainlink / Redstone Oracle\n/// 4. If it passes all checks, return the price. Otherwise use fallbacks, usually to Chainlink. In extreme edge-cases revert.\n/// @dev For UniV3 with check mode, if fetching the check price fails, the UniV3 rate is used directly.\ncontract UniV3CheckCLRSOracle is FluidOracle, UniV3OracleImpl, FallbackOracleImpl {\n /// @dev Rate check oracle delta percent in 1e2 percent. If current uniswap price is out of this delta,\n /// current price fetching reverts.\n uint256 internal immutable _RATE_CHECK_MAX_DELTA_PERCENT;\n\n /// @dev which oracle to use as final rate source:\n /// - 1 = UniV3 ONLY (no check),\n /// - 2 = UniV3 with Chainlink / Redstone check\n /// - 3 = Chainlink / Redstone with UniV3 used as check.\n uint8 internal immutable _RATE_SOURCE;\n\n struct UniV3CheckCLRSConstructorParams {\n /// @param uniV3Params UniV3Oracle constructor params struct.\n UniV3ConstructorParams uniV3Params;\n /// @param chainlinkParams ChainlinkOracle constructor params struct for UniV3CheckCLRSOracle.\n ChainlinkConstructorParams chainlinkParams;\n /// @param redstoneOracle Redstone Oracle data for UniV3CheckCLRSOracle. (address can be set to zero address if using Chainlink only)\n RedstoneOracleData redstoneOracle;\n /// @param rateSource which oracle to use as final rate source for UniV3CheckCLRSOracle:\n /// - 1 = UniV3 ONLY (no check),\n /// - 2 = UniV3 with Chainlink / Redstone check\n /// - 3 = Chainlink / Redstone with UniV3 used as check.\n uint8 rateSource;\n /// @param fallbackMainSource which oracle to use as CL/RS main source for UniV3CheckCLRSOracle: see FallbackOracleImpl constructor `mainSource_`\n uint8 fallbackMainSource;\n /// @param rateCheckMaxDeltaPercent Rate check oracle delta in 1e2 percent for UniV3CheckCLRSOracle\n uint256 rateCheckMaxDeltaPercent;\n }\n\n constructor(\n UniV3CheckCLRSConstructorParams memory params_\n )\n UniV3OracleImpl(params_.uniV3Params)\n FallbackOracleImpl(params_.fallbackMainSource, params_.chainlinkParams, params_.redstoneOracle)\n {\n if (\n params_.rateSource < 1 ||\n params_.rateSource > 3 ||\n params_.rateCheckMaxDeltaPercent > OracleUtils.HUNDRED_PERCENT_DELTA_SCALER ||\n // Chainlink only Oracle with UniV3 check. Delta would be ignored so revert this type of Oracle setup.\n (params_.fallbackMainSource == 1 && params_.rateSource == 3)\n ) {\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidParams);\n }\n\n _RATE_CHECK_MAX_DELTA_PERCENT = params_.rateCheckMaxDeltaPercent;\n _RATE_SOURCE = params_.rateSource;\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @notice returns all oracle related data as utility for easy off-chain / block explorer use in a single view method\n function uniV3CheckOracleData()\n public\n view\n returns (uint256 rateCheckMaxDelta_, uint256 rateSource_, uint256 fallbackMainSource_)\n {\n return (_RATE_CHECK_MAX_DELTA_PERCENT, _RATE_SOURCE, _FALLBACK_ORACLE_MAIN_SOURCE);\n }\n\n function _getExchangeRate() internal view returns (uint256 exchangeRate_) {\n if (_RATE_SOURCE == 1) {\n // uniswap is the only main source without check:\n // 1. get uniV3 rate.\n // 2. If that fails (outside delta range) -> revert (no other Oracle configured).\n exchangeRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n // fetching UniV3 failed or invalid delta -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n return exchangeRate_;\n }\n\n uint256 checkRate_;\n bool fallback_;\n if (_RATE_SOURCE == 2) {\n // uniswap is main source, with Chainlink / Redstone as check\n // 1. get uniV3 rate\n\n // case uniV3 rate fails (outside delta range):\n // 2. get Chainlink rate. -> if successful, use Chainlink as result\n // 3. if Chainlink fails too, get Redstone -> if successful, use Redstone as result\n // 4. if Redstone fails too, revert\n\n // case if uniV3 rate is ok\n // 2. get Chainlink or Redstone rate for check (one is configured as main check source, other one is fallback source)\n // -> if both fail to fetch, use uniV3 rate directly.\n // 3. check the delta for uniV3 rate against the check soure rate. -> if ok, return uniV3 rate\n // 4. if delta check fails, check delta against the fallback check source. -> if ok, return uniV3 rate\n // 5. if delta check fails for both sources, return Chainlink price\n\n exchangeRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n // uniV3 failed or invalid delta -> use (Chainlink with Redstone as fallback)\n exchangeRate_ = _getChainlinkOrRedstoneAsFallback();\n if (exchangeRate_ == 0) {\n // Chainlink / Redstone failed too -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n return exchangeRate_;\n }\n\n (checkRate_, fallback_) = _getRateWithFallback();\n if (checkRate_ == 0) {\n // check price source failed to fetch -> directly use uniV3 TWAP checked price\n // Note uniV3 price fetching was successful, would have been caught otherwise above.\n return exchangeRate_;\n }\n } else {\n // Chainlink / Redstone is main source, with uniV3 as check.\n // 1. get Chainlink / Redstone rate (one is configured as main source, other one is fallback source)\n\n // case when both Chainlink & Redstone fail:\n // 2. get uniV3 rate. if successful, use uniV3 rate. otherwise, revert (all oracles failed).\n\n // case when Chainlink / Redstone fetch is successful:\n // 2. get uniV3 rate for check.\n // 3. if uniV3 rate fails to fetch (outside delta), use Chainlink / Redstone directly (skip check).\n // 4. if uniV3 rate is ok, check the delta for Chainlink / Redstone rate against uniV3 rate.\n // -> if ok, return Chainlink / Redstone (main) rate\n // 5. if delta check fails, check delta against the fallback main source.\n // -> if ok, return fallback main rate\n // 6. if delta check fails for both sources, return Chainlink price.\n\n (exchangeRate_, fallback_) = _getRateWithFallback();\n checkRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n if (checkRate_ == 0) {\n // all oracles failed, revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // Both Chainlink & Redstone failed -> directly use uniV3 TWAP checked price\n // Note uniV3 price fetching was successful, would have been caught otherwise above.\n return checkRate_;\n }\n\n if (checkRate_ == 0) {\n // uniV3 failed -> skip check against Uniswap price.\n\n return exchangeRate_;\n }\n }\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n if (fallback_) {\n // fallback already used, no other rate available to check.\n\n // if price is chainlink price -> return it.\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 3) {\n // redstone with Chainlink as fallback\n return _RATE_SOURCE == 2 ? checkRate_ : exchangeRate_; // if rate source is 2, Chainlink rate is in checkRate_\n }\n\n // if price is redstone price -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidPrice);\n }\n\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 1) {\n // 1 = only chainlink and UniV3 is configured and delta check failed. no fallback available.\n if (_RATE_SOURCE == 2) {\n // case where uniV3 is main source with only Chainlink as check rate Oracle configured.\n // delta check failed -> return Chainlink price (instead of uniV3 price).\n return checkRate_;\n }\n\n // here: if (_FALLBACK_ORACLE_MAIN_SOURCE == 1 && _RATE_SOURCE == 3)\n // rate source is 3: Chainlink as main, uniV3 as delta. delta check failed.\n // this Oracle type would basically be a more expensive Chainlink-only Oracle because the delta check against UniV3 is ignored.\n // this setup is reverted in constructor, but in any case returning Chainlink price here even though this code should never be reached.\n return exchangeRate_; // exchangeRate_ here is chainlink price\n }\n\n // fallback not done yet -> check against fallback price.\n // So if originally Chainlink was fetched and delta failed, check against Redstone.\n // if originally Redstone was fetched and delta failed, check against Chainlink.\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 2) {\n // 2 = Chainlink with Redstone Fallback. delta check against Chainlink failed. try against Redstone.\n uint256 redstoneRate_ = _getRedstoneExchangeRate();\n uint256 chainlinkRate_;\n if (_RATE_SOURCE == 2) {\n // uniV3 main source. -> update checkRate_ with Redstone price\n chainlinkRate_ = checkRate_;\n checkRate_ = redstoneRate_;\n } else {\n // uniV3 is check source. -> update exchangeRate_ with Redstone price\n chainlinkRate_ = exchangeRate_;\n exchangeRate_ = redstoneRate_;\n }\n\n if (redstoneRate_ == 0) {\n // fetching Redstone failed. So delta UniV3 <> Chainlink failed, fetching Redstone as backup failed.\n // -> return chainlink price (for both cases when Chainlink is main and when UniV3 is the main source).\n return chainlinkRate_;\n }\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n // delta check against Redstone failed too. return Chainlink price\n return chainlinkRate_;\n }\n\n // delta check against Redstone passed. if uniV3 main source -> return uniV3, else return Redstone.\n // exchangeRate_ is already set correctly for this.\n } else {\n // 3 = Redstone with Chainlink Fallback. delta check against Redstone failed. try against Chainlink.\n uint256 chainlinkRate_ = _getChainlinkExchangeRate();\n if (chainlinkRate_ == 0) {\n // fetching Chainlink failed. So delta UniV3 <> Redstone failed, fetching Chainlink as backup check failed.\n // -> revert.\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidPrice);\n }\n\n if (_RATE_SOURCE == 3) {\n // uniV3 is check source. -> update exchangeRate_ with Chainlink price.\n // Optimization: in this case we can directly return chainlink price, because if delta check between\n // Chainlink (new main source) and uniV3 (check source) fails, we anyway return Chainlink price still.\n return chainlinkRate_;\n }\n\n // uniV3 main source. -> update checkRate_ with Chainlink price and compare delta again\n checkRate_ = chainlinkRate_;\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n // delta check against Chainlink failed too. case here can only be where uniV3 would have been\n // main source and Chainlink check source. -> return Chainlink as price instead of uniV3\n return checkRate_;\n }\n\n // delta check against Chainlink passed. if uniV3 main source -> return uniV3, else return Chainlink.\n // exchangeRate_ is already set correctly for this.\n }\n }\n }\n}\n" }, "contracts/oracle/oracles/weETHOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\n\n/// @title WeETHOracle\n/// @notice Gets the exchange rate between weETH and eETH directly from the weETH contract.\ncontract WeETHOracle is FluidOracle, WeETHOracleImpl {\n /// @notice constructor sets the weETH `weETH_` token address.\n constructor(IWeETH weETH_) WeETHOracleImpl(weETH_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getWeETHExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n" }, "contracts/oracle/oracles/weETHUniV3CheckCLRSOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { UniV3CheckCLRSOracle } from \"./uniV3CheckCLRSOracle.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title weETHOracle combined with a uniV3CheckCLRSOracle.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// 1. weETH Oracle price for weETH -> eETH = ETH (pegged)\n/// 2. result from 1. combined with a uniV3CheckCLRSOracle to get someToken (e.g. ETH) -> someToken2.\n/// e.g. when going from weETH to USDC:\n/// 1. weETH -> eETH = ETH via weETH Oracle\n/// 2. ETH -> USDC via UniV3 ETH <> USDC pool checked against ETH -> USDC Chainlink feed.\ncontract WeETHUniV3CheckCLRSOracle is FluidOracle, WeETHOracleImpl, UniV3CheckCLRSOracle {\n /// @notice constructs a WeETHUniV3CheckCLRSOracle with all inherited contracts\n /// @param weETH_ address of the weETH contract\n /// @param uniV3CheckCLRSParams_ UniV3CheckCLRSOracle constructor params\n constructor(\n IWeETH weETH_,\n UniV3CheckCLRSConstructorParams memory uniV3CheckCLRSParams_\n ) WeETHOracleImpl(weETH_) UniV3CheckCLRSOracle(uniV3CheckCLRSParams_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0.\n // (super.getExchangeRate() returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n // Combine this rate with the weETH -> eETH = ETH rate.\n exchangeRate_ =\n (super.getExchangeRateOperate() * _getWeETHExchangeRate()) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0.\n // (super.getExchangeRate() returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n // Combine this rate with the weETH -> eETH = ETH rate.\n exchangeRate_ =\n (super.getExchangeRateLiquidate() * _getWeETHExchangeRate()) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view override(FluidOracle, UniV3CheckCLRSOracle) returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n" }, "contracts/oracle/oracles/weETHwstETHOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title Oracle for weETH (Etherfi's wrapped eETH) to wstETH. wstETH is the debt token here (get amount of wstETH for 1 weETH)\ncontract WeETHWstETHOracle is FluidOracle, WstETHOracleImpl, WeETHOracleImpl {\n /// @param wstETH address of the wstETH contract\n /// @param weETH address of the weETH contract\n constructor(IWstETH wstETH, IWeETH weETH) WstETHOracleImpl(wstETH) WeETHOracleImpl(weETH) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n // weEth -> wstETH\n exchangeRate_ =\n (_WEETH.getEETHByWeETH(1e18) * (10 ** OracleUtils.RATE_OUTPUT_DECIMALS)) /\n _WSTETH.stEthPerToken();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n" }, "contracts/oracle/oracles/wstETHCLRS2UniV3CheckCLRSOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { UniV3CheckCLRSOracle } from \"./uniV3CheckCLRSOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { FallbackOracleImpl2 } from \"../implementations/fallbackOracleImpl2.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n// @dev uses FallbackOracleImpl2 to avoid conflicts with already used ChainlinkOracleImpl, RedstoneOracleImpl and\n// FallbackOracleImpl in UniV3CheckCLRSOracle.\n\n/// @title wstETHCLRSOracle combined with a uniV3CheckCLRSOracle.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// 1. wstETH Oracle price in combination with rate from Chainlink price feeds (or Redstone as fallback).\n/// combining those two into one rate resulting in wstETH <> someToken\n/// 2. result from 1. combined with a uniV3CheckCLRSOracle to get from someToken <> someToken2\n/// e.g. when going from wstETH to USDC:\n/// 1. wstETH -> stETH wstETH Oracle, stETH -> ETH Chainlink feed.\n/// 2. ETH -> USDC via UniV3 ETH <> USDC pool checked against ETH -> USDC Chainlink feed.\ncontract WstETHCLRS2UniV3CheckCLRSOracle is FluidOracle, WstETHOracleImpl, FallbackOracleImpl2, UniV3CheckCLRSOracle {\n struct WstETHCLRS2ConstructorParams {\n /// @param wstETH address of the wstETH contract\n IWstETH wstETH;\n /// @param fallbackMainSource which oracle to use as main source for wstETH <> CLRS: 1 = Chainlink, 2 = Redstone (other one is fallback).\n uint8 fallbackMainSource;\n /// @param chainlinkParams chainlink Oracle constructor params struct for wstETH <> CLRS.\n ChainlinkConstructorParams chainlinkParams;\n /// @param redstoneOracle Redstone Oracle data for wstETH <> CLRS. (address can be set to zero address if using Chainlink only)\n RedstoneOracleData redstoneOracle;\n }\n\n /// @notice constructs a WstETHCLRS2UniV3CheckCLRSOracle with all inherited contracts\n /// @param wstETHCLRS2Params_ WstETHCLRS2ConstructorParams for wstETH <> CLRS Token2 conversion\n /// @param uniV3CheckCLRSParams_ UniV3CheckCLRSOracle constructor params\n constructor(\n WstETHCLRS2ConstructorParams memory wstETHCLRS2Params_,\n UniV3CheckCLRSConstructorParams memory uniV3CheckCLRSParams_\n )\n WstETHOracleImpl(wstETHCLRS2Params_.wstETH)\n FallbackOracleImpl2(\n wstETHCLRS2Params_.fallbackMainSource,\n wstETHCLRS2Params_.chainlinkParams,\n wstETHCLRS2Params_.redstoneOracle\n )\n UniV3CheckCLRSOracle(uniV3CheckCLRSParams_)\n {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate for stETH <> CLRS feed. uses FallbackOracleImpl2\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // 2. combine CLRS feed price with wstETH price to have wstETH <> stETH <> SomeToken fully converted\n exchangeRate_ = (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n // 3. get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0. Combine this rate with existing.\n // (super.getExchangeRate returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n exchangeRate_ = (super.getExchangeRateOperate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate for stETH <> CLRS feed. uses FallbackOracleImpl2\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // 2. combine CLRS feed price with wstETH price to have wstETH <> stETH <> SomeToken fully converted\n exchangeRate_ = (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n // 3. get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0. Combine this rate with existing.\n // (super.getExchangeRate returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n exchangeRate_ = (super.getExchangeRateLiquidate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view override(FluidOracle, UniV3CheckCLRSOracle) returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE2_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE2_MAIN_SOURCE;\n }\n}\n" }, "contracts/oracle/oracles/wstETHCLRSOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title WstETH Chainlink / Redstone Oracle (with fallback)\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// wstETH Oracle price in combination with rate from Chainlink price feeds (or Redstone as fallback),\n/// hopping the 2 rates into 1 rate.\n/// e.g. when going from wstETH to USDT:\n/// wstETH -> stETH wstETH Oracle, stETH -> ETH Chainlink feed, ETH -> USDT Chainlink feed.\ncontract WstETHCLRSOracle is FluidOracle, WstETHOracleImpl, FallbackOracleImpl {\n /// @notice sets the wstETH address, main source, Chainlink Oracle and Redstone Oracle data.\n /// @param wstETH_ address of the wstETH contract\n /// @param mainSource_ which oracle to use as main source: 1 = Chainlink, 2 = Redstone (other one is fallback).\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n IWstETH wstETH_,\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n ) WstETHOracleImpl(wstETH_) FallbackOracleImpl(mainSource_, chainlinkParams_, redstoneOracle_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n (exchangeRate_, ) = _getRateWithFallback();\n\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRSOracle__ExchangeRateZero);\n }\n\n return (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE_MAIN_SOURCE;\n }\n}\n" }, "contracts/oracle/oracles/wstETHOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\n\n/// @title WstETHOracle\n/// @notice Gets the exchange rate between wstETH and stETH directly from the wstETH contract.\ncontract WstETHOracle is FluidOracle, WstETHOracleImpl {\n /// @notice constructor sets the wstETH `wstETH_` token address.\n constructor(IWstETH wstETH_) WstETHOracleImpl(wstETH_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getWstETHExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n" }, "contracts/periphery/liquidation/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Owned } from \"solmate/src/auth/Owned.sol\";\n\nimport { IFluidVaultT1 } from \"../../protocols/vault/interfaces/iVaultT1.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/interfaces/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IWETH9 } from \"../../protocols/lending/interfaces/external/iWETH9.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\ninterface InstaFlashInterface {\n function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external;\n}\n\ninterface InstaFlashReceiverInterface {\n function executeOperation(\n address[] calldata assets,\n uint256[] calldata amounts,\n uint256[] calldata premiums,\n address initiator,\n bytes calldata _data\n ) external returns (bool);\n}\n\ncontract VaultT1Liquidator is Owned {\n using SafeERC20 for IERC20;\n\n address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n InstaFlashInterface immutable public FLA;\n IWETH9 immutable public WETH;\n\n mapping (address => bool) public rebalancer; \n\n error FluidVaultT1Liquidator__InvalidOperation();\n\n event Liquidated(\n address indexed vault,\n uint256 collateral,\n uint256 debt\n );\n\n event Withdraw(\n address indexed to,\n address indexed token,\n uint256 amount\n );\n\n event ToggleRebalancer(\n address indexed rebalancer,\n bool indexed status\n );\n\n struct LiquidationParams {\n address vault;\n address supply;\n address borrow;\n uint256 supplyAmount;\n uint256 borrowAmount;\n uint256 colPerUnitDebt;\n bool absorb;\n address swapRouter;\n address swapApproval;\n bytes swapData;\n uint256 route;\n }\n\n constructor (\n address owner_,\n address fla_,\n address weth_,\n address[] memory rebalancers_\n ) Owned(owner_) {\n FLA = InstaFlashInterface(fla_);\n WETH = IWETH9(weth_);\n for (uint256 i = 0; i < rebalancers_.length; i++) {\n rebalancer[rebalancers_[i]] = true;\n emit ToggleRebalancer(rebalancers_[i], true);\n }\n }\n\n modifier isRebalancer() {\n if (!rebalancer[msg.sender] && msg.sender != owner) {\n revert FluidVaultT1Liquidator__InvalidOperation();\n }\n _;\n }\n\n function toggleRebalancer(address rebalancer_, bool status_) public onlyOwner {\n rebalancer[rebalancer_] = status_;\n emit ToggleRebalancer(rebalancer_, status_);\n }\n\n function spell(address[] memory targets_, bytes[] memory calldatas_) public onlyOwner {\n for (uint256 i = 0; i < targets_.length; i++) {\n Address.functionDelegateCall(targets_[i], calldatas_[i]);\n }\n }\n\n function withdraw(address to_, address[] memory tokens_, uint256[] memory amounts_) public onlyOwner {\n for (uint i = 0; i < tokens_.length; i++) {\n if (tokens_[i] == ETH_ADDRESS) {\n Address.sendValue(payable(to_), amounts_[i]);\n } else {\n IERC20(tokens_[i]).safeTransfer(to_, amounts_[i]);\n }\n emit Withdraw(to_, tokens_[i], amounts_[i]);\n }\n }\n \n function liquidation(LiquidationParams memory params_) public isRebalancer {\n address[] memory tokens = new address[](1);\n uint256[] memory amts = new uint256[](1);\n\n // Take flashloan in borrow token of the vault\n tokens[0] = params_.borrow == ETH_ADDRESS ? address(WETH) : params_.borrow;\n amts[0] = params_.borrowAmount;\n\n bytes memory data_ = abi.encode(params_);\n\n FLA.flashLoan(tokens, amts, params_.route, data_, abi.encode());\n\n }\n\n function executeOperation(\n address[] calldata assets,\n uint256[] calldata amounts,\n uint256[] calldata premiums,\n address initiator,\n bytes calldata _data\n ) external returns (bool) {\n if (msg.sender != address(FLA)) revert FluidVaultT1Liquidator__InvalidOperation();\n if (initiator != address(this)) revert FluidVaultT1Liquidator__InvalidOperation();\n LiquidationParams memory params_ = abi.decode(_data, (LiquidationParams));\n\n {\n uint256 value_;\n if (params_.borrow != ETH_ADDRESS) {\n IERC20(params_.borrow).safeApprove(params_.vault, 0);\n IERC20(params_.borrow).safeApprove(params_.vault, params_.borrowAmount);\n value_ = 0;\n } else {\n WETH.withdraw(params_.borrowAmount);\n value_ = params_.borrowAmount;\n }\n\n (uint256 debtAmount_, uint256 collateralAmount_) = IFluidVaultT1(params_.vault).liquidate{value: value_}(params_.borrowAmount, params_.colPerUnitDebt, address(this), params_.absorb);\n\n if (params_.supply != ETH_ADDRESS) {\n IERC20(params_.supply).safeApprove(params_.swapApproval, 0);\n IERC20(params_.supply).safeApprove(params_.swapApproval, params_.supplyAmount);\n value_ = 0;\n } else {\n value_ = params_.supplyAmount;\n }\n\n Address.functionCallWithValue(params_.swapRouter, params_.swapData, value_, \"Swap: failed\");\n emit Liquidated(params_.vault, collateralAmount_, debtAmount_);\n }\n\n uint256 flashloanAmount_ = amounts[0] + premiums[0] + 10;\n if (params_.borrow == ETH_ADDRESS) {\n uint256 wethBalance_ = WETH.balanceOf(address(this));\n if (wethBalance_ < flashloanAmount_) {\n WETH.deposit{value: flashloanAmount_ - wethBalance_}();\n }\n }\n IERC20(assets[0]).safeTransfer(msg.sender, flashloanAmount_);\n\n return true;\n }\n\n receive() payable external {}\n}" }, "contracts/periphery/migration/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Owned } from \"solmate/src/auth/Owned.sol\";\n\nimport { IFluidVaultT1 } from \"../../protocols/vault/interfaces/iVaultT1.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/interfaces/IERC20.sol\";\nimport { IERC721 } from \"@openzeppelin/contracts/interfaces/IERC721.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IWETH9 } from \"../../protocols/lending/interfaces/external/iWETH9.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IFluidVaultFactory } from \"../../protocols/vault/interfaces/iVaultFactory.sol\";\n\ninterface InstaFlashInterface {\n function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external;\n}\n\ninterface InstaFlashReceiverInterface {\n function executeOperation(\n address[] calldata assets,\n uint256[] calldata amounts,\n uint256[] calldata premiums,\n address initiator,\n bytes calldata _data\n ) external returns (bool);\n}\n\ncontract VaultT1Migrator is Owned {\n using SafeERC20 for IERC20;\n\n uint internal constant X32 = 0xffffffff;\n address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n IFluidVaultFactory immutable public VAULT_T1_FACTORY_OLD;\n IFluidVaultFactory immutable public VAULT_T1_FACTORY_NEW;\n\n InstaFlashInterface immutable public FLA;\n IWETH9 immutable public WETH;\n\n struct FlashloanConfig {\n uint256 amount;\n uint256 route;\n }\n\n mapping (address => FlashloanConfig) public flashloanConfig;\n\n error FluidVaultT1Migrator__InvalidOperation();\n error FluidVaultT1Migrator__NotAllowed();\n\n event Migrated(\n uint256 indexed vaultId,\n address indexed owner,\n uint256 indexed nft,\n uint256 collateral,\n uint256 debt\n );\n\n event Withdraw(\n address indexed to,\n address indexed token,\n uint256 amount\n );\n\n event SetFlashloanConfig (\n address indexed token, \n uint256 indexed route,\n uint256 amount\n );\n\n constructor(\n address owner_,\n address fla_,\n address weth_,\n address oldFactory_,\n address newFactory_\n ) Owned(owner_) {\n FLA = InstaFlashInterface(fla_);\n WETH = IWETH9(weth_);\n\n VAULT_T1_FACTORY_OLD = IFluidVaultFactory(oldFactory_);\n VAULT_T1_FACTORY_NEW = IFluidVaultFactory(newFactory_);\n }\n\n function setFlashloanConfig(address token_, uint256 route_, uint256 amount_) public onlyOwner {\n flashloanConfig[token_] = FlashloanConfig({\n amount: amount_,\n route: route_\n });\n emit SetFlashloanConfig(token_, route_, amount_);\n }\n\n function spell(address[] memory targets_, bytes[] memory calldatas_) public onlyOwner {\n for (uint256 i = 0; i < targets_.length; i++) {\n Address.functionDelegateCall(targets_[i], calldatas_[i]);\n }\n }\n\n function withdraw(address to_, address[] memory tokens_, uint256[] memory amounts_) public onlyOwner {\n for (uint i = 0; i < tokens_.length; i++) {\n if (tokens_[i] == ETH_ADDRESS) {\n Address.sendValue(payable(to_), amounts_[i]);\n } else {\n IERC20(tokens_[i]).safeTransfer(to_, amounts_[i]);\n }\n emit Withdraw(to_, tokens_[i], amounts_[i]);\n }\n }\n\n struct MigratorParams {\n uint256 nftId;\n address owner;\n address supply;\n address borrow;\n uint256 vaultId;\n address vaultFrom;\n address vaultTo;\n uint256 supplyAmount;\n uint256 borrowAmount;\n int256 withdrawAmount;\n int256 paybackAmount;\n uint256 route;\n }\n\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4) {\n if (msg.sender != address(VAULT_T1_FACTORY_OLD)) revert FluidVaultT1Migrator__NotAllowed();\n if (operator != from) revert FluidVaultT1Migrator__NotAllowed();\n\n MigratorParams memory params_;\n params_.nftId = tokenId;\n params_.owner = from;\n\n (params_.vaultId, params_.vaultFrom) = vaultByNftId(tokenId);\n (params_.supply, params_.borrow) = vaultConfig(params_.vaultFrom);\n params_.vaultTo = getVaultAddress(address(VAULT_T1_FACTORY_NEW), params_.vaultId);\n\n if (data.length > 0) {\n (params_.route, params_.borrowAmount) = abi.decode(data, (uint256, uint256));\n } else {\n FlashloanConfig memory c_ = flashloanConfig[params_.borrow];\n (params_.route, params_.borrowAmount) = (c_.route, c_.amount);\n }\n\n if (params_.route == 0) revert FluidVaultT1Migrator__NotAllowed();\n\n address[] memory tokens = new address[](1);\n uint256[] memory amts = new uint256[](1);\n\n // Take flashloan in borrow token of the vault\n tokens[0] = params_.borrow == ETH_ADDRESS ? address(WETH) : params_.borrow;\n amts[0] = params_.borrowAmount * 150 / 100; // increase by 50%\n\n bytes memory data_ = abi.encode(params_);\n\n FLA.flashLoan(tokens, amts, params_.route, data_, abi.encode());\n\n if (VAULT_T1_FACTORY_NEW.balanceOf(address(this)) > 0) revert FluidVaultT1Migrator__NotAllowed();\n \n return this.onERC721Received.selector;\n }\n\n function executeOperation(\n address[] calldata assets,\n uint256[] calldata amounts,\n uint256[] calldata premiums,\n address initiator,\n bytes calldata _data\n ) external returns (bool) {\n if (msg.sender != address(FLA)) revert FluidVaultT1Migrator__NotAllowed();\n if (initiator != address(this)) revert FluidVaultT1Migrator__NotAllowed();\n MigratorParams memory params_ = abi.decode(_data, (MigratorParams));\n\n uint256 value_;\n {\n if (params_.borrow != ETH_ADDRESS) {\n IERC20(params_.borrow).safeApprove(params_.vaultFrom, 0);\n IERC20(params_.borrow).safeApprove(params_.vaultFrom, params_.borrowAmount);\n value_ = 0;\n } else {\n WETH.withdraw(params_.borrowAmount);\n value_ = params_.borrowAmount;\n }\n\n (\n ,\n params_.withdrawAmount,\n params_.paybackAmount\n ) = IFluidVaultT1(params_.vaultFrom).operate{value: value_}(\n params_.nftId,\n type(int256).min,\n type(int256).min,\n address(this)\n );\n\n params_.withdrawAmount = -params_.withdrawAmount;\n params_.paybackAmount = -params_.paybackAmount;\n params_.supplyAmount = uint256(params_.withdrawAmount);\n params_.borrowAmount = uint256(params_.paybackAmount);\n }\n\n {\n if (params_.supply != ETH_ADDRESS) {\n IERC20(params_.supply).safeApprove(params_.vaultTo, 0);\n IERC20(params_.supply).safeApprove(params_.vaultTo, params_.supplyAmount);\n value_ = 0;\n } else {\n value_ = params_.supplyAmount;\n }\n\n (\n uint256 nftId_,\n ,\n\n ) = IFluidVaultT1(params_.vaultTo).operate{value: value_}(\n 0,\n params_.withdrawAmount,\n params_.paybackAmount,\n address(this)\n\n );\n\n IERC721(VAULT_T1_FACTORY_NEW).transferFrom(address(this), params_.owner, nftId_);\n }\n\n uint256 flashloanAmount_ = amounts[0] + premiums[0] + 10;\n if (params_.borrow == ETH_ADDRESS) {\n uint256 wethBalance_ = WETH.balanceOf(address(this));\n if (wethBalance_ < flashloanAmount_) {\n WETH.deposit{value: flashloanAmount_ - wethBalance_}();\n }\n }\n IERC20(assets[0]).safeTransfer(msg.sender, flashloanAmount_);\n\n emit Migrated(\n params_.vaultId,\n params_.owner,\n params_.nftId,\n uint256(-params_.withdrawAmount),\n uint256(-params_.paybackAmount)\n );\n\n return true;\n }\n\n function vaultByNftId(uint nftId_) public view returns (uint256 vaultId_, address vault_) {\n uint tokenConfig_ = VAULT_T1_FACTORY_OLD.readFromStorage(calculateStorageSlotUintMapping(3, nftId_));\n vaultId_ = (tokenConfig_ >> 192) & X32;\n vault_ = getVaultAddress(address(VAULT_T1_FACTORY_OLD), vaultId_);\n }\n\n function vaultConfig(address vault_) public view returns(address supply_, address borrow_) {\n IFluidVaultT1.ConstantViews memory constants_ = IFluidVaultT1(vault_).constantsView();\n\n supply_ = constants_.supplyToken;\n borrow_ = constants_.borrowToken;\n }\n\n function calculateStorageSlotUintMapping(uint256 slot_, uint key_) public pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n function getVaultAddress(address vaultFactory_, uint256 vaultId_) public pure returns (address vault_) {\n // @dev based on https://ethereum.stackexchange.com/a/61413\n bytes memory data;\n if (vaultId_ == 0x00) {\n // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment\n return address(0);\n } else if (vaultId_ <= 0x7f) {\n data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), address(vaultFactory_), uint8(vaultId_));\n } else if (vaultId_ <= 0xff) {\n data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), address(vaultFactory_), bytes1(0x81), uint8(vaultId_));\n } else if (vaultId_ <= 0xffff) {\n data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), address(vaultFactory_), bytes1(0x82), uint16(vaultId_));\n } else if (vaultId_ <= 0xffffff) {\n data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), address(vaultFactory_), bytes1(0x83), uint24(vaultId_));\n } else {\n data = abi.encodePacked(bytes1(0xda), bytes1(0x94), address(vaultFactory_), bytes1(0x84), uint32(vaultId_));\n }\n\n return address(uint160(uint256(keccak256(data))));\n }\n\n receive() payable external {}\n}" }, "contracts/periphery/resolvers/lending/iLendingResolver.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { IAllowanceTransfer } from \"../../../protocols/lending/interfaces/permit2/iAllowanceTransfer.sol\";\nimport { IFluidLendingFactory } from \"../../../protocols/lending/interfaces/iLendingFactory.sol\";\nimport { IFluidLendingRewardsRateModel } from \"../../../protocols/lending/interfaces/iLendingRewardsRateModel.sol\";\nimport { IFToken } from \"../../../protocols/lending/interfaces/iFToken.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { IFluidLiquidityResolver } from \"../../../periphery/resolvers/liquidity/iLiquidityResolver.sol\";\n\ninterface IFluidLendingResolver {\n /// @notice returns the lending factory address\n function LENDING_FACTORY() external view returns (IFluidLendingFactory);\n\n /// @notice returns the liquidity resolver address\n function LIQUIDITY_RESOLVER() external view returns (IFluidLiquidityResolver);\n\n /// @notice returns all fToken types at the `LENDING_FACTORY`\n function getAllFTokenTypes() external view returns (string[] memory);\n\n /// @notice returns all created fTokens at the `LENDING_FACTORY`\n function getAllFTokens() external view returns (address[] memory);\n\n /// @notice reads if a certain `auth_` address is an allowed auth or not. Owner is auth by default.\n function isLendingFactoryAuth(address auth_) external view returns (bool);\n\n /// @notice reads if a certain `deployer_` address is an allowed deployer or not. Owner is deployer by default.\n function isLendingFactoryDeployer(address deployer_) external view returns (bool);\n\n /// @notice computes deterministic token address for `asset_` for a lending protocol\n /// @param asset_ address of the asset\n /// @param fTokenType_ type of fToken:\n /// - if underlying asset supports EIP-2612, the fToken should be type `EIP2612Deposits`\n /// - otherwise it should use `Permit2Deposits`\n /// - if it's the native token, it should use `NativeUnderlying`\n /// - could be more types available, check `fTokenTypes()`\n /// @return token_ detemrinistic address of the computed token\n function computeFToken(address asset_, string calldata fTokenType_) external view returns (address);\n\n /// @notice gets all public details for a certain `fToken_`, such as\n /// fToken type, name, symbol, decimals, underlying asset, total amounts, convertTo values, rewards.\n /// Note it also returns whether the fToken supports deposits / mints via EIP-2612, but it is not a 100% guarantee!\n /// To make sure, check for the underlying if it supports EIP-2612 manually.\n /// @param fToken_ the fToken to get the details for\n /// @return fTokenDetails_ retrieved FTokenDetails struct\n function getFTokenDetails(IFToken fToken_) external view returns (Structs.FTokenDetails memory fTokenDetails_);\n\n /// @notice returns config, rewards and exchange prices data of an fToken.\n /// @param fToken_ the fToken to get the data for\n /// @return liquidity_ address of the Liquidity contract.\n /// @return lendingFactory_ address of the Lending factory contract.\n /// @return lendingRewardsRateModel_ address of the rewards rate model contract. changeable by LendingFactory auths.\n /// @return permit2_ address of the Permit2 contract used for deposits / mint with signature\n /// @return rebalancer_ address of the rebalancer allowed to execute `rebalance()`\n /// @return rewardsActive_ true if rewards are currently active\n /// @return liquidityBalance_ current Liquidity supply balance of `address(this)` for the underyling asset\n /// @return liquidityExchangePrice_ (updated) exchange price for the underlying assset in the liquidity protocol (without rewards)\n /// @return tokenExchangePrice_ (updated) exchange price between fToken and the underlying assset (with rewards)\n function getFTokenInternalData(\n IFToken fToken_\n )\n external\n view\n returns (\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IFluidLendingRewardsRateModel lendingRewardsRateModel_,\n IAllowanceTransfer permit2_,\n address rebalancer_,\n bool rewardsActive_,\n uint256 liquidityBalance_,\n uint256 liquidityExchangePrice_,\n uint256 tokenExchangePrice_\n );\n\n /// @notice gets all public details for all itokens, such as\n /// fToken type, name, symbol, decimals, underlying asset, total amounts, convertTo values, rewards\n function getFTokensEntireData() external view returns (Structs.FTokenDetails[] memory);\n\n /// @notice gets all public details for all itokens, such as\n /// fToken type, name, symbol, decimals, underlying asset, total amounts, convertTo values, rewards\n /// and user position for each token\n function getUserPositions(address user_) external view returns (Structs.FTokenDetailsUserPosition[] memory);\n\n /// @notice gets rewards related data: the `rewardsRateModel_` contract and the current `rewardsRate_` for the `fToken_`\n function getFTokenRewards(\n IFToken fToken_\n ) external view returns (IFluidLendingRewardsRateModel rewardsRateModel_, uint256 rewardsRate_);\n\n /// @notice gets rewards rate model config constants\n function getFTokenRewardsRateModelConfig(\n IFToken fToken_\n )\n external\n view\n returns (\n uint256 duration_,\n uint256 startTime_,\n uint256 endTime_,\n uint256 startTvl_,\n uint256 maxRate_,\n uint256 rewardAmount_,\n address initiator_\n );\n\n /// @notice gets a `user_` position for an `fToken_`.\n /// @return userPosition user position struct\n function getUserPosition(\n IFToken fToken_,\n address user_\n ) external view returns (Structs.UserPosition memory userPosition);\n\n /// @notice gets `fToken_` preview amounts for `assets_` or `shares_`.\n /// @return previewDeposit_ preview for deposit of `assets_`\n /// @return previewMint_ preview for mint of `shares_`\n /// @return previewWithdraw_ preview for withdraw of `assets_`\n /// @return previewRedeem_ preview for redeem of `shares_`\n function getPreviews(\n IFToken fToken_,\n uint256 assets_,\n uint256 shares_\n )\n external\n view\n returns (uint256 previewDeposit_, uint256 previewMint_, uint256 previewWithdraw_, uint256 previewRedeem_);\n}\n" }, "contracts/periphery/resolvers/lending/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20Permit } from \"@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { LiquidityCalcs } from \"../../../libraries/liquidityCalcs.sol\";\nimport { IFluidLendingFactory } from \"../../../protocols/lending/interfaces/iLendingFactory.sol\";\nimport { IFluidLendingRewardsRateModel } from \"../../../protocols/lending/interfaces/iLendingRewardsRateModel.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { IAllowanceTransfer } from \"../../../protocols/lending/interfaces/permit2/iAllowanceTransfer.sol\";\nimport { IFToken, IFTokenNativeUnderlying } from \"../../../protocols/lending/interfaces/iFToken.sol\";\nimport { IFluidLiquidityResolver } from \"../../../periphery/resolvers/liquidity/iLiquidityResolver.sol\";\nimport { Structs as LiquidityStructs } from \"../../../periphery/resolvers/liquidity/structs.sol\";\nimport { IFluidLendingResolver } from \"./iLendingResolver.sol\";\nimport { Structs } from \"./structs.sol\";\n\n/// @notice Fluid Lending protocol (fTokens) resolver\n/// Implements various view-only methods to give easy access to Lending protocol data.\ncontract FluidLendingResolver is IFluidLendingResolver, Structs {\n /// @dev address that is mapped to the chain native token\n address internal constant _NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @inheritdoc IFluidLendingResolver\n IFluidLendingFactory public immutable LENDING_FACTORY;\n\n /// @inheritdoc IFluidLendingResolver\n IFluidLiquidityResolver public immutable LIQUIDITY_RESOLVER;\n\n /// @notice thrown if an input param address is zero\n error FluidLendingResolver__AddressZero();\n\n /// @notice constructor sets the immutable `LENDING_FACTORY` and `LIQUIDITY_RESOLVER` address\n constructor(IFluidLendingFactory lendingFactory_, IFluidLiquidityResolver liquidityResolver_) {\n if (address(lendingFactory_) == address(0) || address(liquidityResolver_) == address(0)) {\n revert FluidLendingResolver__AddressZero();\n }\n LENDING_FACTORY = lendingFactory_;\n LIQUIDITY_RESOLVER = liquidityResolver_;\n }\n\n /// @inheritdoc IFluidLendingResolver\n function isLendingFactoryAuth(address auth_) external view returns (bool) {\n return LENDING_FACTORY.isAuth(auth_);\n }\n\n /// @inheritdoc IFluidLendingResolver\n function isLendingFactoryDeployer(address deployer_) external view returns (bool) {\n return LENDING_FACTORY.isDeployer(deployer_);\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getAllFTokenTypes() public view returns (string[] memory) {\n return LENDING_FACTORY.fTokenTypes();\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getAllFTokens() public view returns (address[] memory) {\n return LENDING_FACTORY.allTokens();\n }\n\n /// @inheritdoc IFluidLendingResolver\n function computeFToken(address asset_, string calldata fTokenType_) external view returns (address) {\n return LENDING_FACTORY.computeToken(asset_, fTokenType_);\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getFTokenDetails(IFToken fToken_) public view returns (FTokenDetails memory fTokenDetails_) {\n address underlying_ = fToken_.asset();\n\n bool isNativeUnderlying_ = false;\n try IFTokenNativeUnderlying(address(fToken_)).NATIVE_TOKEN_ADDRESS() {\n // if NATIVE_TOKEN_ADDRESS is defined, fTokenType must be NativeUnderlying.\n isNativeUnderlying_ = true;\n } catch {}\n\n bool supportsEIP2612Deposits_ = false;\n try IERC20Permit(underlying_).DOMAIN_SEPARATOR() {\n // if DOMAIN_SEPARATOR is defined, we assume underlying supports EIP2612. Not a 100% guarantee\n supportsEIP2612Deposits_ = true;\n } catch {}\n\n (, uint256 rewardsRate_) = getFTokenRewards(fToken_);\n (\n LiquidityStructs.UserSupplyData memory userSupplyData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n ) = LIQUIDITY_RESOLVER.getUserSupplyData(\n address(fToken_),\n isNativeUnderlying_ ? _NATIVE_TOKEN_ADDRESS : underlying_\n );\n\n uint256 totalAssets_ = fToken_.totalAssets();\n\n fTokenDetails_ = FTokenDetails(\n address(fToken_),\n supportsEIP2612Deposits_,\n isNativeUnderlying_,\n fToken_.name(),\n fToken_.symbol(),\n fToken_.decimals(),\n underlying_,\n totalAssets_,\n fToken_.totalSupply(),\n fToken_.convertToShares(10 ** fToken_.decimals()), // example convertToShares for 10 ** decimals\n fToken_.convertToAssets(10 ** fToken_.decimals()), // example convertToAssets for 10 ** decimals\n rewardsRate_,\n overallTokenData_.supplyRate,\n int256(userSupplyData_.supply) - int256(totalAssets_), // rebalanceDifference\n userSupplyData_\n );\n\n return fTokenDetails_;\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getFTokenInternalData(\n IFToken fToken_\n )\n public\n view\n returns (\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IFluidLendingRewardsRateModel lendingRewardsRateModel_,\n IAllowanceTransfer permit2_,\n address rebalancer_,\n bool rewardsActive_,\n uint256 liquidityBalance_,\n uint256 liquidityExchangePrice_,\n uint256 tokenExchangePrice_\n )\n {\n return fToken_.getData();\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getFTokensEntireData() public view returns (FTokenDetails[] memory) {\n address[] memory allTokens = getAllFTokens();\n FTokenDetails[] memory fTokenDetailsArr_ = new FTokenDetails[](allTokens.length);\n for (uint256 i = 0; i < allTokens.length; ) {\n fTokenDetailsArr_[i] = getFTokenDetails(IFToken(allTokens[i]));\n unchecked {\n i++;\n }\n }\n return fTokenDetailsArr_;\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getUserPositions(address user_) external view returns (FTokenDetailsUserPosition[] memory) {\n FTokenDetails[] memory fTokensEntireData_ = getFTokensEntireData();\n FTokenDetailsUserPosition[] memory userPositionArr_ = new FTokenDetailsUserPosition[](\n fTokensEntireData_.length\n );\n for (uint256 i = 0; i < fTokensEntireData_.length; ) {\n userPositionArr_[i].fTokenDetails = fTokensEntireData_[i];\n userPositionArr_[i].userPosition = getUserPosition(IFToken(fTokensEntireData_[i].tokenAddress), user_);\n unchecked {\n i++;\n }\n }\n return userPositionArr_;\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getFTokenRewards(\n IFToken fToken_\n ) public view returns (IFluidLendingRewardsRateModel rewardsRateModel_, uint256 rewardsRate_) {\n bool rewardsActive_;\n (, , rewardsRateModel_, , , rewardsActive_, , , ) = fToken_.getData();\n\n if (rewardsActive_ && address(rewardsRateModel_) != address(0)) {\n (rewardsRate_, , ) = rewardsRateModel_.getRate(fToken_.totalAssets());\n }\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getFTokenRewardsRateModelConfig(\n IFToken fToken_\n )\n public\n view\n returns (\n uint256 duration_,\n uint256 startTime_,\n uint256 endTime_,\n uint256 startTvl_,\n uint256 maxRate_,\n uint256 rewardAmount_,\n address initiator_\n )\n {\n IFluidLendingRewardsRateModel rewardsRateModel_;\n (, , rewardsRateModel_, , , , , , ) = fToken_.getData();\n\n if (address(rewardsRateModel_) != address(0)) {\n (duration_, startTime_, endTime_, startTvl_, maxRate_, rewardAmount_, initiator_) = rewardsRateModel_\n .getConfig();\n }\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getUserPosition(IFToken fToken_, address user_) public view returns (UserPosition memory userPosition) {\n IERC20 underlying_ = IERC20(fToken_.asset());\n\n userPosition.fTokenShares = fToken_.balanceOf(user_);\n userPosition.underlyingAssets = fToken_.convertToAssets(userPosition.fTokenShares);\n userPosition.underlyingBalance = underlying_.balanceOf(user_);\n userPosition.allowance = underlying_.allowance(user_, address(fToken_));\n }\n\n /// @inheritdoc IFluidLendingResolver\n function getPreviews(\n IFToken fToken_,\n uint256 assets_,\n uint256 shares_\n )\n public\n view\n returns (uint256 previewDeposit_, uint256 previewMint_, uint256 previewWithdraw_, uint256 previewRedeem_)\n {\n previewDeposit_ = fToken_.previewDeposit(assets_);\n previewMint_ = fToken_.previewMint(shares_);\n previewWithdraw_ = fToken_.previewWithdraw(assets_);\n previewRedeem_ = fToken_.previewRedeem(shares_);\n }\n}\n" }, "contracts/periphery/resolvers/lending/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLendingFactory } from \"../../../protocols/lending/interfaces/iLendingFactory.sol\";\nimport { Structs as FluidLiquidityResolverStructs } from \"../liquidity/structs.sol\";\n\nabstract contract Structs {\n struct FTokenDetails {\n address tokenAddress;\n bool eip2612Deposits;\n bool isNativeUnderlying;\n string name;\n string symbol;\n uint256 decimals;\n address asset;\n uint256 totalAssets;\n uint256 totalSupply;\n uint256 convertToShares;\n uint256 convertToAssets;\n // additional yield from rewards, if active\n uint256 rewardsRate;\n // yield at Liquidity\n uint256 supplyRate;\n // difference between fToken assets & actual deposit at Liquidity. (supplyAtLiquidity - totalAssets).\n // if negative, rewards must be funded to guarantee withdrawal is possible for all users. This happens\n // by executing rebalance().\n int256 rebalanceDifference;\n // liquidity related data such as supply amount, limits, expansion etc.\n FluidLiquidityResolverStructs.UserSupplyData liquidityUserSupplyData;\n }\n\n struct UserPosition {\n uint256 fTokenShares;\n uint256 underlyingAssets;\n uint256 underlyingBalance;\n uint256 allowance;\n }\n\n struct FTokenDetailsUserPosition {\n FTokenDetails fTokenDetails;\n UserPosition userPosition;\n }\n}\n" }, "contracts/periphery/resolvers/liquidity/iLiquidityResolver.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { Structs as LiquidityStructs } from \"../../../periphery/resolvers/liquidity/structs.sol\";\n\ninterface IFluidLiquidityResolver {\n /// @notice gets the `revenueAmount_` for a `token_`.\n function getRevenue(address token_) external view returns (uint256 revenueAmount_);\n\n /// @notice address of contract that gets sent the revenue. Configurable by governance\n function getRevenueCollector() external view returns (address);\n\n /// @notice Liquidity contract paused status: status = 1 -> normal. status = 2 -> paused.\n function getStatus() external view returns (uint256);\n\n /// @notice checks if `auth_` is an allowed auth on Liquidity.\n /// Auths can set most config values. E.g. contracts that automate certain flows like e.g. adding a new fToken.\n /// Governance can add/remove auths. Governance is auth by default.\n function isAuth(address auth_) external view returns (uint256);\n\n /// @notice checks if `guardian_` is an allowed Guardian on Liquidity.\n /// Guardians can pause lower class users.\n /// Governance can add/remove guardians. Governance is guardian by default.\n function isGuardian(address guardian_) external view returns (uint256);\n\n /// @notice gets user class for `user_`. Class defines which protocols can be paused by guardians.\n /// Currently there are 2 classes: 0 can be paused by guardians. 1 cannot be paused by guardians.\n /// New protocols are added as class 0 and will be upgraded to 1 over time.\n function getUserClass(address user_) external view returns (uint256);\n\n /// @notice gets exchangePricesAndConfig packed uint256 storage slot for `token_`.\n function getExchangePricesAndConfig(address token_) external view returns (uint256);\n\n /// @notice gets rateConfig packed uint256 storage slot for `token_`.\n function getRateConfig(address token_) external view returns (uint256);\n\n /// @notice gets totalAmounts packed uint256 storage slot for `token_`.\n function getTotalAmounts(address token_) external view returns (uint256);\n\n /// @notice gets configs2 packed uint256 storage slot for `token_`.\n function getConfigs2(address token_) external view returns (uint256);\n\n /// @notice gets userSupply data packed uint256 storage slot for `user_` and `token_`.\n function getUserSupply(address user_, address token_) external view returns (uint256);\n\n /// @notice gets userBorrow data packed uint256 storage slot for `user_` and `token_`.\n function getUserBorrow(address user_, address token_) external view returns (uint256);\n\n /// @notice returns all `listedTokens_` at the Liquidity contract. Once configured, a token can never be removed.\n function listedTokens() external view returns (address[] memory listedTokens_);\n\n /// @notice get the Rate config data `rateData_` for a `token_` compiled from the packed uint256 rateConfig storage slot\n function getTokenRateData(address token_) external view returns (LiquidityStructs.RateData memory rateData_);\n\n /// @notice get the Rate config datas `rateDatas_` for multiple `tokens_` compiled from the packed uint256 rateConfig storage slot\n function getTokensRateData(\n address[] calldata tokens_\n ) external view returns (LiquidityStructs.RateData[] memory rateDatas_);\n\n /// @notice returns general data for `token_` such as rates, exchange prices, utilization, fee, total amounts etc.\n function getOverallTokenData(\n address token_\n ) external view returns (LiquidityStructs.OverallTokenData memory overallTokenData_);\n\n /// @notice returns general data for multiple `tokens_` such as rates, exchange prices, utilization, fee, total amounts etc.\n function getOverallTokensData(\n address[] calldata tokens_\n ) external view returns (LiquidityStructs.OverallTokenData[] memory overallTokensData_);\n\n /// @notice returns general data for all `listedTokens()` such as rates, exchange prices, utilization, fee, total amounts etc.\n function getAllOverallTokensData()\n external\n view\n returns (LiquidityStructs.OverallTokenData[] memory overallTokensData_);\n\n /// @notice returns `user_` supply data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for `token_`\n function getUserSupplyData(\n address user_,\n address token_\n )\n external\n view\n returns (\n LiquidityStructs.UserSupplyData memory userSupplyData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n );\n\n /// @notice returns `user_` supply data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for multiple `tokens_`\n function getUserMultipleSupplyData(\n address user_,\n address[] calldata tokens_\n )\n external\n view\n returns (\n LiquidityStructs.UserSupplyData[] memory userSuppliesData_,\n LiquidityStructs.OverallTokenData[] memory overallTokensData_\n );\n\n /// @notice returns `user_` borrow data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for `token_`\n function getUserBorrowData(\n address user_,\n address token_\n )\n external\n view\n returns (\n LiquidityStructs.UserBorrowData memory userBorrowData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n );\n\n /// @notice returns `user_` borrow data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for multiple `tokens_`\n function getUserMultipleBorrowData(\n address user_,\n address[] calldata tokens_\n )\n external\n view\n returns (\n LiquidityStructs.UserBorrowData[] memory userBorrowingsData_,\n LiquidityStructs.OverallTokenData[] memory overallTokensData_\n );\n\n /// @notice returns `user_` supply data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for multiple `supplyTokens_`\n /// and returns `user_` borrow data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for multiple `borrowTokens_`\n function getUserMultipleBorrowSupplyData(\n address user_,\n address[] calldata supplyTokens_,\n address[] calldata borrowTokens_\n )\n external\n view\n returns (\n LiquidityStructs.UserSupplyData[] memory userSuppliesData_,\n LiquidityStructs.OverallTokenData[] memory overallSupplyTokensData_,\n LiquidityStructs.UserBorrowData[] memory userBorrowingsData_,\n LiquidityStructs.OverallTokenData[] memory overallBorrowTokensData_\n );\n}\n" }, "contracts/periphery/resolvers/liquidity/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { LiquidityCalcs } from \"../../../libraries/liquidityCalcs.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { IFluidLiquidityResolver } from \"./iLiquidityResolver.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { Variables } from \"./variables.sol\";\n\ninterface TokenInterface {\n function balanceOf(address) external view returns (uint);\n}\n\n/// @notice Fluid Liquidity resolver\n/// Implements various view-only methods to give easy access to Liquidity data.\ncontract FluidLiquidityResolver is IFluidLiquidityResolver, Variables, Structs {\n /// @dev address that is mapped to the chain native token\n address internal constant _NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @notice thrown if an input param address is zero\n error FluidLiquidityResolver__AddressZero();\n\n constructor(IFluidLiquidity liquidity_) Variables(liquidity_) {\n if (address(liquidity_) == address(0)) {\n revert FluidLiquidityResolver__AddressZero();\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getRevenueCollector() public view returns (address) {\n return address(uint160(LIQUIDITY.readFromStorage(bytes32(0))));\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getRevenue(address token_) public view returns (uint256 revenueAmount_) {\n uint256 liquidityTokenBalance_ = token_ == _NATIVE_TOKEN_ADDRESS\n ? address(LIQUIDITY).balance\n : IERC20(token_).balanceOf(address(LIQUIDITY));\n\n uint256 exchangePricesAndConfig_ = getExchangePricesAndConfig(token_);\n if (exchangePricesAndConfig_ == 0) {\n return 0;\n }\n\n return LiquidityCalcs.calcRevenue(getTotalAmounts(token_), exchangePricesAndConfig_, liquidityTokenBalance_);\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getStatus() public view returns (uint256) {\n return LIQUIDITY.readFromStorage(bytes32(LiquiditySlotsLink.LIQUIDITY_STATUS_SLOT));\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function isAuth(address auth_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(LiquiditySlotsLink.LIQUIDITY_AUTHS_MAPPING_SLOT, auth_)\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function isGuardian(address guardian_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_GUARDIANS_MAPPING_SLOT,\n guardian_\n )\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getUserClass(address user_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_CLASS_MAPPING_SLOT,\n user_\n )\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getExchangePricesAndConfig(address token_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n token_\n )\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getRateConfig(address token_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_RATE_DATA_MAPPING_SLOT,\n token_\n )\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getTotalAmounts(address token_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n token_\n )\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getConfigs2(address token_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_CONFIGS2_MAPPING_SLOT,\n token_\n )\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getUserSupply(address user_, address token_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT,\n user_,\n token_\n )\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getUserBorrow(address user_, address token_) public view returns (uint256) {\n return\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT,\n user_,\n token_\n )\n );\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function listedTokens() public view returns (address[] memory listedTokens_) {\n uint256 length_ = LIQUIDITY.readFromStorage(bytes32(LiquiditySlotsLink.LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT));\n\n listedTokens_ = new address[](length_);\n\n uint256 startingSlotForArrayElements_ = uint256(\n keccak256(abi.encode(LiquiditySlotsLink.LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT))\n );\n\n for (uint256 i; i < length_; i++) {\n listedTokens_[i] = address(uint160(LIQUIDITY.readFromStorage(bytes32(startingSlotForArrayElements_ + i))));\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getTokenRateData(address token_) public view returns (RateData memory rateData_) {\n uint256 rateConfig_ = getRateConfig(token_);\n\n rateData_.version = rateConfig_ & 0xF;\n\n if (rateData_.version == 1) {\n rateData_.rateDataV1.rateAtUtilizationZero =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) &\n X16;\n rateData_.rateDataV1.kink = (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16;\n rateData_.rateDataV1.rateAtUtilizationKink =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) &\n X16;\n rateData_.rateDataV1.rateAtUtilizationMax =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) &\n X16;\n } else if (rateData_.version == 2) {\n rateData_.rateDataV2.rateAtUtilizationZero =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) &\n X16;\n rateData_.rateDataV2.kink1 =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) &\n X16;\n rateData_.rateDataV2.rateAtUtilizationKink1 =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) &\n X16;\n rateData_.rateDataV2.kink2 =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) &\n X16;\n rateData_.rateDataV2.rateAtUtilizationKink2 =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) &\n X16;\n rateData_.rateDataV2.rateAtUtilizationMax =\n (rateConfig_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) &\n X16;\n } else if (rateData_.version > 0) {\n // when version is 0 -> token not configured yet. do not revert, just return 0 for all values\n revert(\"not-valid-rate-version\");\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getTokensRateData(address[] calldata tokens_) public view returns (RateData[] memory rateDatas_) {\n uint256 length_ = tokens_.length;\n rateDatas_ = new RateData[](length_);\n\n for (uint256 i; i < length_; i++) {\n rateDatas_[i] = getTokenRateData(tokens_[i]);\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getOverallTokenData(\n address token_\n ) public view returns (Structs.OverallTokenData memory overallTokenData_) {\n overallTokenData_.rateData = getTokenRateData(token_);\n\n uint256 exchangePriceAndConfig_ = getExchangePricesAndConfig(token_);\n if (exchangePriceAndConfig_ > 0) {\n uint256 totalAmounts_ = getTotalAmounts(token_);\n\n (overallTokenData_.supplyExchangePrice, overallTokenData_.borrowExchangePrice) = LiquidityCalcs\n .calcExchangePrices(exchangePriceAndConfig_);\n\n overallTokenData_.borrowRate = exchangePriceAndConfig_ & X16;\n overallTokenData_.fee = (exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14;\n overallTokenData_.lastStoredUtilization =\n (exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) &\n X14;\n overallTokenData_.storageUpdateThreshold =\n (exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD) &\n X14;\n overallTokenData_.lastUpdateTimestamp =\n (exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) &\n X33;\n overallTokenData_.maxUtilization = FOUR_DECIMALS;\n if ((exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_USES_CONFIGS2) & 1 == 1) {\n overallTokenData_.maxUtilization = getConfigs2(token_) & X14;\n }\n\n // Extract supply & borrow amounts\n uint256 temp_ = totalAmounts_ & X64;\n overallTokenData_.supplyRawInterest = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n temp_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;\n overallTokenData_.supplyInterestFree = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n temp_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;\n overallTokenData_.borrowRawInterest = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n temp_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);\n overallTokenData_.borrowInterestFree = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n uint256 supplyWithInterest_;\n uint256 borrowWithInterest_;\n if (overallTokenData_.supplyRawInterest > 0) {\n // use old exchange prices for supply rate to be at same level as borrow rate from storage.\n // Note the rate here can be a tiny bit with higher precision because we use borrowWithInterest_ / supplyWithInterest_\n // which has higher precision than the utilization used from storage in LiquidityCalcs\n supplyWithInterest_ =\n (overallTokenData_.supplyRawInterest *\n ((exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &\n X64)) /\n EXCHANGE_PRICES_PRECISION; // normalized from raw\n borrowWithInterest_ =\n (overallTokenData_.borrowRawInterest *\n ((exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &\n X64)) /\n EXCHANGE_PRICES_PRECISION; // normalized from raw\n\n overallTokenData_.supplyRate = supplyWithInterest_ == 0\n ? 0\n : (overallTokenData_.borrowRate * (FOUR_DECIMALS - overallTokenData_.fee) * borrowWithInterest_) /\n (supplyWithInterest_ * FOUR_DECIMALS);\n }\n\n supplyWithInterest_ =\n (overallTokenData_.supplyRawInterest * overallTokenData_.supplyExchangePrice) /\n EXCHANGE_PRICES_PRECISION; // normalized from raw\n overallTokenData_.totalSupply = supplyWithInterest_ + overallTokenData_.supplyInterestFree;\n borrowWithInterest_ =\n (overallTokenData_.borrowRawInterest * overallTokenData_.borrowExchangePrice) /\n EXCHANGE_PRICES_PRECISION; // normalized from raw\n overallTokenData_.totalBorrow = borrowWithInterest_ + overallTokenData_.borrowInterestFree;\n\n overallTokenData_.revenue = getRevenue(token_);\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getOverallTokensData(\n address[] memory tokens_\n ) public view returns (Structs.OverallTokenData[] memory overallTokensData_) {\n uint256 length_ = tokens_.length;\n overallTokensData_ = new Structs.OverallTokenData[](length_);\n for (uint256 i; i < length_; i++) {\n overallTokensData_[i] = getOverallTokenData(tokens_[i]);\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getAllOverallTokensData() public view returns (Structs.OverallTokenData[] memory overallTokensData_) {\n return getOverallTokensData(listedTokens());\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getUserSupplyData(\n address user_,\n address token_\n )\n public\n view\n returns (Structs.UserSupplyData memory userSupplyData_, Structs.OverallTokenData memory overallTokenData_)\n {\n overallTokenData_ = getOverallTokenData(token_);\n uint256 userSupply_ = getUserSupply(user_, token_);\n\n if (userSupply_ > 0) {\n // if userSupply_ == 0 -> user not configured yet for token at Liquidity\n userSupplyData_.modeWithInterest = userSupply_ & 1 == 1;\n userSupplyData_.supply = BigMathMinified.fromBigNumber(\n (userSupply_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n // get updated expanded withdrawal limit\n userSupplyData_.withdrawalLimit = LiquidityCalcs.calcWithdrawalLimitBeforeOperate(\n userSupply_,\n userSupplyData_.supply\n );\n\n userSupplyData_.lastUpdateTimestamp =\n (userSupply_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) &\n X33;\n userSupplyData_.expandPercent = (userSupply_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;\n userSupplyData_.expandDuration = (userSupply_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24;\n userSupplyData_.baseWithdrawalLimit = BigMathMinified.fromBigNumber(\n (userSupply_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (userSupplyData_.modeWithInterest) {\n // convert raw amounts to normal for withInterest mode\n userSupplyData_.supply =\n (userSupplyData_.supply * overallTokenData_.supplyExchangePrice) /\n EXCHANGE_PRICES_PRECISION;\n userSupplyData_.withdrawalLimit =\n (userSupplyData_.withdrawalLimit * overallTokenData_.supplyExchangePrice) /\n EXCHANGE_PRICES_PRECISION;\n userSupplyData_.baseWithdrawalLimit =\n (userSupplyData_.baseWithdrawalLimit * overallTokenData_.supplyExchangePrice) /\n EXCHANGE_PRICES_PRECISION;\n }\n\n userSupplyData_.withdrawableUntilLimit = userSupplyData_.supply > userSupplyData_.withdrawalLimit\n ? userSupplyData_.supply - userSupplyData_.withdrawalLimit\n : 0;\n uint balanceOf_ = token_ == _NATIVE_TOKEN_ADDRESS\n ? address(LIQUIDITY).balance\n : TokenInterface(token_).balanceOf(address(LIQUIDITY));\n\n userSupplyData_.withdrawable = balanceOf_ > userSupplyData_.withdrawableUntilLimit\n ? userSupplyData_.withdrawableUntilLimit\n : balanceOf_;\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getUserMultipleSupplyData(\n address user_,\n address[] calldata tokens_\n )\n public\n view\n returns (\n Structs.UserSupplyData[] memory userSuppliesData_,\n Structs.OverallTokenData[] memory overallTokensData_\n )\n {\n uint256 length_ = tokens_.length;\n userSuppliesData_ = new Structs.UserSupplyData[](length_);\n overallTokensData_ = new Structs.OverallTokenData[](length_);\n\n for (uint256 i; i < length_; i++) {\n (userSuppliesData_[i], overallTokensData_[i]) = getUserSupplyData(user_, tokens_[i]);\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getUserBorrowData(\n address user_,\n address token_\n )\n public\n view\n returns (Structs.UserBorrowData memory userBorrowData_, Structs.OverallTokenData memory overallTokenData_)\n {\n overallTokenData_ = getOverallTokenData(token_);\n uint256 userBorrow_ = getUserBorrow(user_, token_);\n\n if (userBorrow_ > 0) {\n // if userBorrow_ == 0 -> user not configured yet for token at Liquidity\n\n userBorrowData_.modeWithInterest = userBorrow_ & 1 == 1;\n\n userBorrowData_.borrow = BigMathMinified.fromBigNumber(\n (userBorrow_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n // get updated expanded borrow limit\n userBorrowData_.borrowLimit = LiquidityCalcs.calcBorrowLimitBeforeOperate(\n userBorrow_,\n userBorrowData_.borrow\n );\n\n userBorrowData_.lastUpdateTimestamp =\n (userBorrow_ >> LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) &\n X33;\n userBorrowData_.expandPercent = (userBorrow_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;\n userBorrowData_.expandDuration = (userBorrow_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24;\n userBorrowData_.baseBorrowLimit = BigMathMinified.fromBigNumber(\n (userBorrow_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n userBorrowData_.maxBorrowLimit = BigMathMinified.fromBigNumber(\n (userBorrow_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (userBorrowData_.modeWithInterest) {\n // convert raw amounts to normal for withInterest mode\n userBorrowData_.borrow =\n (userBorrowData_.borrow * overallTokenData_.borrowExchangePrice) /\n EXCHANGE_PRICES_PRECISION;\n userBorrowData_.borrowLimit =\n (userBorrowData_.borrowLimit * overallTokenData_.borrowExchangePrice) /\n EXCHANGE_PRICES_PRECISION;\n userBorrowData_.baseBorrowLimit =\n (userBorrowData_.baseBorrowLimit * overallTokenData_.borrowExchangePrice) /\n EXCHANGE_PRICES_PRECISION;\n userBorrowData_.maxBorrowLimit =\n (userBorrowData_.maxBorrowLimit * overallTokenData_.borrowExchangePrice) /\n EXCHANGE_PRICES_PRECISION;\n }\n\n userBorrowData_.borrowLimitUtilization =\n (overallTokenData_.maxUtilization * overallTokenData_.totalSupply) /\n 1e4;\n\n // uncollected revenue is counting towards available balanceOf.\n // because of this \"borrowable\" would be showing an amount that can go above 100% utilization, causing a revert.\n // need to take into consideration the borrowable amount until the max utilization limit, which depends on the total\n // borrow amount (not user specific)\n uint borrowableUntilUtilizationLimit_ = userBorrowData_.borrowLimitUtilization >\n overallTokenData_.totalBorrow\n ? userBorrowData_.borrowLimitUtilization - overallTokenData_.totalBorrow\n : 0;\n\n uint borrowableUntilBorrowLimit_ = userBorrowData_.borrowLimit > userBorrowData_.borrow\n ? userBorrowData_.borrowLimit - userBorrowData_.borrow\n : 0;\n\n userBorrowData_.borrowableUntilLimit = borrowableUntilBorrowLimit_ > borrowableUntilUtilizationLimit_\n ? borrowableUntilUtilizationLimit_\n : borrowableUntilBorrowLimit_;\n\n // if available balance at Liquidity is less than the borrowableUntilLimit amount, then the balance is\n // the limiting borrowable amount.\n uint balanceOf_ = token_ == _NATIVE_TOKEN_ADDRESS\n ? address(LIQUIDITY).balance\n : TokenInterface(token_).balanceOf(address(LIQUIDITY));\n\n userBorrowData_.borrowable = balanceOf_ > userBorrowData_.borrowableUntilLimit\n ? userBorrowData_.borrowableUntilLimit\n : balanceOf_;\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getUserMultipleBorrowData(\n address user_,\n address[] calldata tokens_\n )\n public\n view\n returns (\n Structs.UserBorrowData[] memory userBorrowingsData_,\n Structs.OverallTokenData[] memory overallTokensData_\n )\n {\n uint256 length_ = tokens_.length;\n userBorrowingsData_ = new UserBorrowData[](length_);\n overallTokensData_ = new Structs.OverallTokenData[](length_);\n\n for (uint256 i; i < length_; i++) {\n (userBorrowingsData_[i], overallTokensData_[i]) = getUserBorrowData(user_, tokens_[i]);\n }\n }\n\n /// @inheritdoc IFluidLiquidityResolver\n function getUserMultipleBorrowSupplyData(\n address user_,\n address[] calldata supplyTokens_,\n address[] calldata borrowTokens_\n )\n public\n view\n returns (\n Structs.UserSupplyData[] memory userSuppliesData_,\n Structs.OverallTokenData[] memory overallSupplyTokensData_,\n Structs.UserBorrowData[] memory userBorrowingsData_,\n Structs.OverallTokenData[] memory overallBorrowTokensData_\n )\n {\n uint256 length_ = supplyTokens_.length;\n userSuppliesData_ = new Structs.UserSupplyData[](length_);\n overallSupplyTokensData_ = new Structs.OverallTokenData[](length_);\n for (uint256 i; i < length_; i++) {\n (userSuppliesData_[i], overallSupplyTokensData_[i]) = getUserSupplyData(user_, supplyTokens_[i]);\n }\n\n length_ = borrowTokens_.length;\n userBorrowingsData_ = new UserBorrowData[](length_);\n overallBorrowTokensData_ = new Structs.OverallTokenData[](length_);\n for (uint256 i; i < length_; i++) {\n (userBorrowingsData_[i], overallBorrowTokensData_[i]) = getUserBorrowData(user_, borrowTokens_[i]);\n }\n }\n}\n" }, "contracts/periphery/resolvers/liquidity/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Structs as AdminModuleStructs } from \"../../../liquidity/adminModule/structs.sol\";\n\nabstract contract Structs {\n struct RateData {\n uint256 version;\n AdminModuleStructs.RateDataV1Params rateDataV1;\n AdminModuleStructs.RateDataV2Params rateDataV2;\n }\n\n struct OverallTokenData {\n uint256 borrowRate;\n uint256 supplyRate;\n uint256 fee; // revenue fee\n uint256 lastStoredUtilization;\n uint256 storageUpdateThreshold;\n uint256 lastUpdateTimestamp;\n uint256 supplyExchangePrice;\n uint256 borrowExchangePrice;\n uint256 supplyRawInterest;\n uint256 supplyInterestFree;\n uint256 borrowRawInterest;\n uint256 borrowInterestFree;\n uint256 totalSupply;\n uint256 totalBorrow;\n uint256 revenue;\n uint256 maxUtilization; // maximum allowed utilization\n RateData rateData;\n }\n\n // amounts are always in normal (for withInterest already multiplied with exchange price)\n struct UserSupplyData {\n bool modeWithInterest; // true if mode = with interest, false = without interest\n uint256 supply; // user supply amount\n // the withdrawal limit (e.g. if 10% is the limit, and 100M is supplied, it would be 90M)\n uint256 withdrawalLimit;\n uint256 lastUpdateTimestamp;\n uint256 expandPercent; // withdrawal limit expand percent in 1e2\n uint256 expandDuration; // withdrawal limit expand duration in seconds\n uint256 baseWithdrawalLimit;\n // the current actual max withdrawable amount (e.g. if 10% is the limit, and 100M is supplied, it would be 10M)\n uint256 withdrawableUntilLimit;\n uint256 withdrawable; // actual currently withdrawable amount (supply - withdrawal Limit) & considering balance\n }\n\n // amounts are always in normal (for withInterest already multiplied with exchange price)\n struct UserBorrowData {\n bool modeWithInterest; // true if mode = with interest, false = without interest\n uint256 borrow; // user borrow amount\n uint256 borrowLimit;\n uint256 lastUpdateTimestamp;\n uint256 expandPercent;\n uint256 expandDuration;\n uint256 baseBorrowLimit;\n uint256 maxBorrowLimit;\n uint256 borrowableUntilLimit; // borrowable amount until any borrow limit (incl. max utilization limit)\n uint256 borrowable; // actual currently borrowable amount (borrow limit - already borrowed) & considering balance, max utilization\n uint256 borrowLimitUtilization; // borrow limit for `maxUtilization`\n }\n}\n" }, "contracts/periphery/resolvers/liquidity/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\n\ncontract Variables {\n /// @dev Storage slot with the admin of the contract. Logic from \"proxy.sol\".\n /// This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n /// validated in the constructor.\n bytes32 internal constant GOVERNANCE_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n // constants used for BigMath conversion from and to storage\n uint256 internal constant SMALL_COEFFICIENT_SIZE = 10;\n uint256 internal constant DEFAULT_COEFFICIENT_SIZE = 56;\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;\n\n uint256 internal constant FOUR_DECIMALS = 10000;\n uint256 internal constant X8 = 0xff;\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n uint256 internal constant X33 = 0x1ffffffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n\n /// @notice address of the liquidity contract\n IFluidLiquidity public immutable LIQUIDITY;\n\n constructor(IFluidLiquidity liquidity_) {\n LIQUIDITY = IFluidLiquidity(liquidity_);\n }\n}\n" }, "contracts/periphery/resolvers/revenue/calcRevenueSimulatedTime.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { LibsErrorTypes as ErrorTypes } from \"../../../libraries/errorTypes.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\n\n/// @dev this is the exact same code as `LiquidityCalcs` library, just that it supports a simulated\n/// block.timestamp to expose historical revenue calculations.\nlibrary CalcRevenueSimulatedTime {\n error FluidLiquidityCalcsRevenueError(uint256 errorId_);\n error FluidCalcRevenueSimulatedTimeInvalidTimestamp();\n\n /// @dev constants as from Liquidity variables.sol\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n // constants used for BigMath conversion from and to storage\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;\n\n uint256 internal constant FOUR_DECIMALS = 1e4;\n uint256 internal constant TWELVE_DECIMALS = 1e12;\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X15 = 0x7fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n uint256 internal constant X33 = 0x1ffffffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC EXCHANGE PRICES /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage.\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param blockTimestamp_ simulated block.timestamp\n /// @return supplyExchangePrice_ updated supplyExchangePrice\n /// @return borrowExchangePrice_ updated borrowExchangePrice\n function calcExchangePrices(\n uint256 exchangePricesAndConfig_,\n uint256 blockTimestamp_\n ) internal pure returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n // Extracting exchange prices\n supplyExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &\n X64;\n borrowExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &\n X64;\n\n if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) {\n revert FluidLiquidityCalcsRevenueError(ErrorTypes.LiquidityCalcs__ExchangePriceZero);\n }\n\n uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate\n\n // @dev HERE CUSTOM: added check for simulated timestamp\n if (\n blockTimestamp_ <\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33)\n ) {\n revert FluidCalcRevenueSimulatedTimeInvalidTimestamp();\n }\n\n unchecked {\n // last timestamp can not be > current timestamp\n uint256 secondsSinceLastUpdate_ = blockTimestamp_ -\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33);\n\n uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) &\n X15;\n if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) {\n // if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed\n // (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // calculate new borrow exchange price.\n // formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_.\n // nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0.\n borrowExchangePrice_ +=\n (borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS);\n\n // FOR SUPPLY EXCHANGE PRICE:\n // all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest.\n // formula: previous price * supply rate * secondsSinceLastUpdate_.\n // where supply rate = (borrow rate - revenueFee%) * ratioSupplyYield. And\n // ratioSupplyYield = utilization * supplyRatio * borrowRatio\n //\n // Example:\n // supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50.\n // BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%.\n // yield is 10 (so half a year must have passed).\n // supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60.\n // borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70.\n // supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2).\n // utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield).\n // borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield):\n // x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%.\n // ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5%\n // supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5%\n // increase in supplyExchangePrice, assuming 100 as previous price.\n // 100 * 22,5% * 1/2 (half a year) = 0,1125.\n // cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20.\n\n // -------------- 1. calculate ratioSupplyYield --------------------------------\n // step1: utilization * supplyRatio (or actually part of lenders receiving yield)\n\n // temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n // if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n // else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15;\n\n if (temp_ == 1) {\n // if no raw supply: no exchange price update needed\n // (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest\n if (temp_ & 1 == 1) {\n // ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = temp_ >> 1;\n\n // Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return\n // in the if statement a little above.\n\n // based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee.\n // supplyRawInterest must become worth 30. totalSupply must become 110.\n // supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2.\n // so ratioSupplyYield must come out as 2.5 (250%).\n // supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted.\n temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision)\n // e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%).\n temp_ =\n // utilization * (100% + 100% / supplyRatio)\n (((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) *\n (1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS);\n // max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31\n } else {\n // ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n temp_ = temp_ >> 1;\n // if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0\n\n // e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%).\n temp_ =\n // 1e27 * utilization * (100% + supplyRatio) / 100%\n (1e27 *\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS + temp_)) /\n (FOUR_DECIMALS * FOUR_DECIMALS);\n // max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27\n }\n // from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31\n\n // step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield)\n if (borrowRatio_ & 1 == 1) {\n // ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n\n // Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered\n // at the beginning of the method by early return if `borrowRatio_ == 1`.\n\n // based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%.\n // so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed.\n // x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666%\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_);\n // max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield).\n } else {\n // ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n // x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%.\n borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_)));\n // borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%.\n // or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101.\n // max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield).\n }\n\n // temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%.\n // max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8\n temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54;\n\n // 2. calculate supply rate\n // temp_ => supply rate (borrow rate - revenueFee%) * ratioSupplyYield.\n // division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate)\n // Note that all calculation divisions for supplyExchangePrice are rounded down.\n // Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest\n // but more suppliers not earning interest.\n temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate\n temp_ * // ratioSupplyYield\n (FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee\n // fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17.\n\n // 3. calculate increase in supply exchange price\n supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS));\n // max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0.\n }\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC REVENUE /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage\n /// and the current balance of the Fluid liquidity contract for the token.\n /// @param totalAmounts_ total amounts packed uint256 read from storage\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))\n /// @param blockTimestamp_ simulated block.timestamp\n /// @return revenueAmount_ collectable revenue amount\n function calcRevenue(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_,\n uint256 blockTimestamp_\n ) internal pure returns (uint256 revenueAmount_) {\n // @dev no need to super-optimize this method as it is only used by admin\n\n // calculate the new exchange prices based on earned interest\n (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(\n exchangePricesAndConfig_,\n blockTimestamp_\n );\n\n // total supply = interest free + with interest converted from raw\n uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_);\n\n if (totalSupply_ > 0) {\n // available revenue: balanceOf(token) + totalBorrowings - totalLendings.\n revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_);\n // ensure there is no possible case because of rounding etc. where this would revert,\n // explicitly check if >\n revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0;\n // Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization\n // can only be revenue.\n } else {\n // if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck\n revenueAmount_ = liquidityTokenBalance_;\n }\n }\n\n /// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_`\n function getTotalSupply(\n uint256 totalAmounts_,\n uint256 supplyExchangePrice_\n ) internal pure returns (uint256 totalSupply_) {\n // totalSupply_ => supplyInterestFree\n totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;\n totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits\n totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalSupply = supplyInterestFree + supplyRawInterest normalized from raw\n totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n\n /// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_`\n function getTotalBorrow(\n uint256 totalAmounts_,\n uint256 borrowExchangePrice_\n ) internal pure returns (uint256 totalBorrow_) {\n // totalBorrow_ => borrowInterestFree\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);\n totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;\n totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw\n totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n}\n" }, "contracts/periphery/resolvers/revenue/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { LiquidityCalcs } from \"../../../libraries/liquidityCalcs.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { CalcRevenueSimulatedTime } from \"./calcRevenueSimulatedTime.sol\";\n\n/// @notice Fluid Revenue resolver\ncontract FluidRevenueResolver {\n /// @notice address of the liquidity contract\n IFluidLiquidity public immutable LIQUIDITY;\n\n /// @dev address that is mapped to the chain native token\n address internal constant _NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n struct TokenRevenue {\n address token;\n uint256 revenueAmount;\n }\n\n constructor(IFluidLiquidity liquidity_) {\n LIQUIDITY = IFluidLiquidity(liquidity_);\n }\n\n /// @notice address of contract that gets sent the revenue. Configurable by governance\n function getRevenueCollector() public view returns (address) {\n return address(uint160(LIQUIDITY.readFromStorage(bytes32(0))));\n }\n\n /// @notice gets the currently uncollected `revenueAmount_` for a `token_`.\n function getRevenue(address token_) public view returns (uint256 revenueAmount_) {\n uint256 exchangePricesAndConfig_ = LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n token_\n )\n );\n if (exchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> revenue is 0\n return 0;\n }\n\n uint256 liquidityTokenBalance_ = token_ == _NATIVE_TOKEN_ADDRESS\n ? address(LIQUIDITY).balance\n : IERC20(token_).balanceOf(address(LIQUIDITY));\n\n uint256 totalAmounts_ = LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n token_\n )\n );\n\n return LiquidityCalcs.calcRevenue(totalAmounts_, exchangePricesAndConfig_, liquidityTokenBalance_);\n }\n\n /// @notice gets the currently uncollected revenues for all listed tokens at Liquidity\n function getRevenues() public view returns (TokenRevenue[] memory tokenRevenues_) {\n uint256 length_ = LIQUIDITY.readFromStorage(bytes32(LiquiditySlotsLink.LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT));\n\n tokenRevenues_ = new TokenRevenue[](length_);\n\n uint256 startingSlotForArrayElements_ = uint256(\n keccak256(abi.encode(LiquiditySlotsLink.LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT))\n );\n\n for (uint256 i; i < length_; i++) {\n tokenRevenues_[i].token = address(\n uint160(LIQUIDITY.readFromStorage(bytes32(startingSlotForArrayElements_ + i)))\n );\n tokenRevenues_[i].revenueAmount = getRevenue(tokenRevenues_[i].token);\n }\n }\n\n /// @notice gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from stacked\n /// uint256 storage slots and the balance of the Fluid liquidity contract for the token.\n /// @dev exposed for advanced revenue calculations\n /// @param totalAmounts_ total amounts packed uint256 read from storage\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))\n /// @return revenueAmount_ collectable revenue amount\n function calcRevenue(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_\n ) public view returns (uint256 revenueAmount_) {\n if (exchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> revenue is 0\n return 0;\n }\n return LiquidityCalcs.calcRevenue(totalAmounts_, exchangePricesAndConfig_, liquidityTokenBalance_);\n }\n\n /// @notice same as `calcRevenue`, but for a simulated `block.timestamp` set via `simulatedTimestamp_`.\n function calcRevenueSimulatedTime(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_,\n uint256 simulatedTimestamp_\n ) public pure returns (uint256 revenueAmount_) {\n if (exchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> revenue is 0\n return 0;\n }\n return\n CalcRevenueSimulatedTime.calcRevenue(\n totalAmounts_,\n exchangePricesAndConfig_,\n liquidityTokenBalance_,\n simulatedTimestamp_\n );\n }\n}\n" }, "contracts/periphery/resolvers/stakingMerkle/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFToken } from \"../../../protocols/lending/interfaces/iFToken.sol\";\nimport { IFluidLendingStakingRewards } from \"../../../protocols/lending/interfaces/iStakingRewards.sol\";\n\n/// @notice Fluid Lending protocol Merkle Staking Rewards resolver\ncontract FluidStakingMerkleResolver {\n IFToken public constant FUSDC = IFToken(0x9Fb7b4477576Fe5B32be4C1843aFB1e55F251B33);\n IFToken public constant FUSDT = IFToken(0x5C20B550819128074FD538Edf79791733ccEdd18);\n\n IFluidLendingStakingRewards public constant FUSDC_STAKING =\n IFluidLendingStakingRewards(0x2fA6c95B69c10f9F52b8990b6C03171F13C46225);\n IFluidLendingStakingRewards public constant FUSDT_STAKING =\n IFluidLendingStakingRewards(0x490681095ed277B45377d28cA15Ac41d64583048);\n\n struct UserPosition {\n address user;\n uint256 shares; // normalShares + stakeShares\n uint256 normalShares;\n uint256 stakeShares;\n }\n\n function getUsersPosition(\n address[] calldata users_,\n IFToken fToken_,\n IFluidLendingStakingRewards stakingContract_\n ) public view returns (UserPosition[] memory positions_) {\n positions_ = new UserPosition[](users_.length);\n\n for (uint256 i; i < users_.length; ++i) {\n positions_[i].user = users_[i];\n positions_[i].normalShares = fToken_.balanceOf(users_[i]);\n positions_[i].stakeShares = stakingContract_.balanceOf(users_[i]);\n positions_[i].shares = positions_[i].normalShares + positions_[i].stakeShares;\n }\n }\n\n function getUsersPositionFUSDC(address[] calldata users_) public view returns (UserPosition[] memory positions_) {\n return getUsersPosition(users_, FUSDC, FUSDC_STAKING);\n }\n\n function getUsersPositionFUSDT(address[] calldata users_) public view returns (UserPosition[] memory positions_) {\n return getUsersPosition(users_, FUSDT, FUSDT_STAKING);\n }\n}\n" }, "contracts/periphery/resolvers/stakingRewards/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20Permit } from \"@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFluidLendingResolver } from \"../lending/iLendingResolver.sol\";\nimport { Structs as FluidLendingResolverStructs } from \"../lending/structs.sol\";\nimport { IFluidLendingStakingRewards } from \"../../../protocols/lending/interfaces/iStakingRewards.sol\";\nimport { Structs } from \"./structs.sol\";\n\n\n/// @notice Fluid Lending protocol Staking Rewards (for fTokens) resolver\n/// Implements various view-only methods to give easy access to Lending protocol staked fToken rewards data.\ncontract FluidStakingRewardsResolver is Structs {\n IFluidLendingResolver immutable public LENDING_RESOLVER;\n\n /// @notice thrown if an input param address is zero\n error FluidStakingRewardsResolver__AddressZero();\n\n constructor(address lendingResolver_) {\n if(lendingResolver_ == address(0)){\n revert FluidStakingRewardsResolver__AddressZero();\n }\n LENDING_RESOLVER = IFluidLendingResolver(lendingResolver_);\n }\n\n function getFTokenStakingRewardsEntireData(address reward_) public view returns (FTokenStakingRewardsDetails memory r_) {\n // if address is 0 then data will be returned as 0\n if (reward_ != address(0)) {\n IFluidLendingStakingRewards rewardContract_ = IFluidLendingStakingRewards(reward_);\n \n r_.rewardPerToken = rewardContract_.rewardPerToken();\n r_.getRewardForDuration = rewardContract_.getRewardForDuration();\n r_.totalSupply = rewardContract_.totalSupply();\n r_.periodFinish = rewardContract_.periodFinish();\n r_.rewardRate = rewardContract_.rewardRate();\n r_.rewardsDuration = rewardContract_.rewardsDuration();\n r_.rewardsToken = address(rewardContract_.rewardsToken());\n r_.fToken = address(rewardContract_.stakingToken());\n }\n }\n\n function getFTokensStakingRewardsEntireData(address[] memory rewards_) public view returns (FTokenStakingRewardsDetails[] memory r_) {\n r_ = new FTokenStakingRewardsDetails[](rewards_.length);\n for (uint i = 0; i < rewards_.length; i++) {\n r_[i] = getFTokenStakingRewardsEntireData(rewards_[i]);\n }\n }\n\n function getUserRewardsData(\n address user_,\n address reward_,\n FluidLendingResolverStructs.FTokenDetails memory fTokenDetails_\n ) public view returns (UserRewardDetails memory u_) {\n if (reward_ != address(0)) {\n IFluidLendingStakingRewards rewardContract_ = IFluidLendingStakingRewards(reward_);\n \n u_.earned = rewardContract_.earned(user_);\n u_.fTokenShares = rewardContract_.balanceOf(user_);\n u_.underlyingAssets = (u_.fTokenShares * fTokenDetails_.convertToAssets) / (10**fTokenDetails_.decimals);\n u_.ftokenAllowance = IERC20(fTokenDetails_.tokenAddress).allowance(user_, reward_);\n }\n }\n\n function getUserAllRewardsData(\n address user_,\n address[] memory rewards_,\n FluidLendingResolverStructs.FTokenDetails[] memory fTokensDetails_\n ) public view returns (UserRewardDetails[] memory u_) {\n u_ = new UserRewardDetails[](rewards_.length);\n for (uint i = 0; i < rewards_.length; i++) {\n u_[i] = getUserRewardsData(user_, rewards_[i], fTokensDetails_[i]);\n }\n }\n\n struct underlyingTokenToRewardsMap {\n address underlyingToken;\n address rewardContract;\n }\n\n function getUserPositions(\n address user_,\n underlyingTokenToRewardsMap[] memory rewardsMap_\n ) public view returns (UserFTokenRewardsEntireData[] memory u_) {\n FluidLendingResolverStructs.FTokenDetailsUserPosition[] memory e_ = LENDING_RESOLVER.getUserPositions(user_);\n uint length_ = e_.length;\n u_ = new UserFTokenRewardsEntireData[](length_);\n\n address rewardToken_;\n for (uint i = 0; i < length_; i++) {\n u_[i].fTokenDetails = e_[i].fTokenDetails;\n u_[i].userPosition = e_[i].userPosition;\n for (uint j = 0; j < rewardsMap_.length; j++) {\n if (u_[i].fTokenDetails.asset == rewardsMap_[j].underlyingToken) {\n rewardToken_ = rewardsMap_[j].rewardContract;\n break;\n }\n }\n u_[i].fTokenRewardsDetails = getFTokenStakingRewardsEntireData(rewardToken_);\n u_[i].userRewardsDetails = getUserRewardsData(user_, rewardToken_, u_[i].fTokenDetails);\n rewardToken_ = address(0);\n }\n }\n\n\n}\n" }, "contracts/periphery/resolvers/stakingRewards/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Structs as FluidLendingResolverStructs } from \"../lending/structs.sol\";\n\nabstract contract Structs {\n struct FTokenStakingRewardsDetails {\n uint rewardPerToken; // how much rewards have distributed per token since start\n uint getRewardForDuration; // total rewards being distributed\n uint totalSupply; // total fToken deposited\n uint periodFinish; // when rewards will get over\n uint rewardRate; // total rewards / duration\n uint rewardsDuration; // how long rewards are for since start to end\n address rewardsToken; // which token are we distributing as rewards\n address fToken; // which token are we distributing as rewards\n }\n\n struct UserRewardDetails {\n uint earned;\n uint fTokenShares; // user fToken balance deposited\n uint underlyingAssets; // user fToken balance converted into underlying token\n uint ftokenAllowance; // allowance of fToken to rewards contract\n }\n\n struct UserFTokenRewardsEntireData {\n FluidLendingResolverStructs.FTokenDetails fTokenDetails;\n FluidLendingResolverStructs.UserPosition userPosition;\n FTokenStakingRewardsDetails fTokenRewardsDetails;\n UserRewardDetails userRewardsDetails;\n }\n}\n" }, "contracts/periphery/resolvers/steth/iStETHResolver.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFluidLiquidityResolver } from \"../liquidity/iLiquidityResolver.sol\";\nimport { IFluidStETHQueue } from \"../../../protocols/steth/interfaces/iStETHQueue.sol\";\nimport { ILidoWithdrawalQueue } from \"../../../protocols/steth/interfaces/external/iLidoWithdrawalQueue.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { Structs as StETHQueueStructs } from \"../../../protocols/steth/structs.sol\";\nimport { Structs as LiquidityStructs } from \"../liquidity/structs.sol\";\n\ninterface IFluidStETHResolver {\n /// @notice address of the stETHQueue contract\n function STETH_QUEUE() external view returns (IFluidStETHQueue);\n\n /// @notice address of the Lido Withdrawal Queue contract (0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1)\n function LIDO_WITHDRAWAL_QUEUE() external view returns (ILidoWithdrawalQueue);\n\n /// @notice address of the LiquidityResolver\n function LIQUIDITY_RESOLVER() external view returns (IFluidLiquidityResolver);\n\n /// @notice returns all constants and config values\n function config()\n external\n view\n returns (\n IFluidLiquidity liquidity_,\n ILidoWithdrawalQueue lidoWithdrawalQueue_,\n IERC20 stETH_,\n address owner_,\n uint16 maxLTV_,\n bool allowListActive_,\n bool isPaused_,\n LiquidityStructs.UserBorrowData memory userBorrowData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n );\n\n /// @notice checks if a linked claim for `claimTo_` and `claimTo_` is ready to be processed at Lido Withdrawal Queue\n /// @param claimTo_ claimTo receiver to process the claim for\n /// @param requestIdFrom_ Lido requestId from (start), as emitted at time of queuing via `LogQueue`\n function isClaimable(address claimTo_, uint256 requestIdFrom_) external view returns (bool);\n\n /// @notice reads if a certain `auth_` address is an allowed auth or not\n function isAuth(address auth_) external view returns (bool);\n\n /// @notice reads if a certain `guardian_` address is an allowed guardian or not\n function isGuardian(address guardian_) external view returns (bool);\n\n /// @notice reads if a certain `user_` address is an allowed user or not\n function isUserAllowed(address user_) external view returns (bool);\n\n /// @notice reads if the protocol is paused or not\n function isPaused() external view returns (bool);\n\n /// @notice reads a Claim struct containing necessary information for executing the claim process from the mapping\n /// claimTo and requestIdFrom -> claims and the claimable status.\n function claim(\n address claimTo_,\n uint256 requestIdFrom_\n ) external view returns (StETHQueueStructs.Claim memory claim_, bool isClaimable_);\n\n /// @notice returns borrow data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for native token\n function getUserBorrowData()\n external\n view\n returns (\n LiquidityStructs.UserBorrowData memory userBorrowData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n );\n}\n" }, "contracts/periphery/resolvers/steth/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFluidLiquidityResolver } from \"../liquidity/iLiquidityResolver.sol\";\nimport { IFluidStETHResolver } from \"./iStETHResolver.sol\";\nimport { IFluidStETHQueue } from \"../../../protocols/steth/interfaces/iStETHQueue.sol\";\nimport { Structs as StETHQueueStructs } from \"../../../protocols/steth/structs.sol\";\nimport { ILidoWithdrawalQueue } from \"../../../protocols/steth/interfaces/external/iLidoWithdrawalQueue.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { Structs as LiquidityStructs } from \"../liquidity/structs.sol\";\n\n/// @notice Fluid StETH protocol resolver\n/// Implements various view-only methods to give easy access to StETH protocol data.\ncontract FluidStETHResolver is IFluidStETHResolver {\n /// @inheritdoc IFluidStETHResolver\n IFluidStETHQueue public immutable STETH_QUEUE;\n\n /// @inheritdoc IFluidStETHResolver\n ILidoWithdrawalQueue public immutable LIDO_WITHDRAWAL_QUEUE;\n\n /// @inheritdoc IFluidStETHResolver\n IFluidLiquidityResolver public immutable LIQUIDITY_RESOLVER;\n\n /// @dev address that is mapped to the chain native token at Liquidity\n address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @notice thrown if an input param address is zero\n error FluidStETHResolver__AddressZero();\n /// @notice thrown if there is no Claim queued for the given input data\n error FluidStETHResolver__NoClaimQueued();\n\n constructor(IFluidStETHQueue stEthQueue_, IFluidLiquidityResolver liquidityResolver_, ILidoWithdrawalQueue lidoWithdrawalQueue_) {\n if (address(stEthQueue_) == address(0) || address(liquidityResolver_) == address(0) || address(lidoWithdrawalQueue_) == address(0)) {\n revert FluidStETHResolver__AddressZero();\n }\n\n STETH_QUEUE = stEthQueue_;\n LIQUIDITY_RESOLVER = liquidityResolver_;\n LIDO_WITHDRAWAL_QUEUE = lidoWithdrawalQueue_;\n }\n\n /// @inheritdoc IFluidStETHResolver\n function isClaimable(address claimTo_, uint256 requestIdFrom_) public view returns (bool) {\n StETHQueueStructs.Claim memory claim_ = STETH_QUEUE.claims(claimTo_, requestIdFrom_);\n\n if (claim_.checkpoint == 0) {\n revert FluidStETHResolver__NoClaimQueued();\n }\n\n uint256 requestsLength_ = claim_.requestIdTo - requestIdFrom_ + 1;\n uint256[] memory requestIds_;\n if (requestsLength_ == 1) {\n // only one request id\n requestIds_ = new uint256[](1);\n requestIds_[0] = requestIdFrom_;\n return LIDO_WITHDRAWAL_QUEUE.getWithdrawalStatus(requestIds_)[0].isFinalized;\n }\n\n // build requestIds array from `requestIdFrom` to `requestIdTo`\n uint256 curRequest_ = requestIdFrom_;\n for (uint256 i; i < requestsLength_; ) {\n requestIds_[i] = curRequest_;\n\n unchecked {\n ++i;\n ++curRequest_;\n }\n }\n\n // get requests statuses\n ILidoWithdrawalQueue.WithdrawalRequestStatus[] memory statuses_ = LIDO_WITHDRAWAL_QUEUE.getWithdrawalStatus(\n requestIds_\n );\n\n // check for each status that it is finalized\n for (uint256 i; i < requestsLength_; ) {\n if (!statuses_[i].isFinalized) {\n return false;\n }\n\n unchecked {\n ++i;\n }\n }\n\n return true;\n }\n\n /// @inheritdoc IFluidStETHResolver\n function config()\n public\n view\n returns (\n IFluidLiquidity liquidity_,\n ILidoWithdrawalQueue lidoWithdrawalQueue_,\n IERC20 stETH_,\n address owner_,\n uint16 maxLTV_,\n bool allowListActive_,\n bool isPaused_,\n LiquidityStructs.UserBorrowData memory userBorrowData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n )\n {\n (liquidity_, lidoWithdrawalQueue_, stETH_) = STETH_QUEUE.constantsView();\n maxLTV_ = STETH_QUEUE.maxLTV();\n allowListActive_ = STETH_QUEUE.allowListActive();\n owner_ = STETH_QUEUE.owner();\n isPaused_ = STETH_QUEUE.isPaused();\n (userBorrowData_, overallTokenData_) = getUserBorrowData();\n }\n\n /// @inheritdoc IFluidStETHResolver\n function isAuth(address auth_) public view returns (bool) {\n return STETH_QUEUE.isAuth(auth_);\n }\n\n /// @inheritdoc IFluidStETHResolver\n function isGuardian(address guardian_) public view returns (bool) {\n return STETH_QUEUE.isGuardian(guardian_);\n }\n\n /// @inheritdoc IFluidStETHResolver\n function isUserAllowed(address user_) public view returns (bool) {\n return STETH_QUEUE.isUserAllowed(user_);\n }\n\n /// @inheritdoc IFluidStETHResolver\n function isPaused() public view returns (bool) {\n return STETH_QUEUE.isPaused();\n }\n\n /// @inheritdoc IFluidStETHResolver\n function claim(\n address claimTo_,\n uint256 requestIdFrom_\n ) public view returns (StETHQueueStructs.Claim memory claim_, bool isClaimable_) {\n claim_ = STETH_QUEUE.claims(claimTo_, requestIdFrom_);\n isClaimable_ = isClaimable(claimTo_, requestIdFrom_);\n }\n\n /// @inheritdoc IFluidStETHResolver\n function getUserBorrowData()\n public\n view\n returns (\n LiquidityStructs.UserBorrowData memory userBorrowData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n )\n {\n (userBorrowData_, overallTokenData_) = LIQUIDITY_RESOLVER.getUserBorrowData(\n address(STETH_QUEUE),\n NATIVE_TOKEN_ADDRESS\n );\n }\n}\n" }, "contracts/periphery/resolvers/vault/helpers.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"./variables.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { TickMath } from \"../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\n\ncontract Helpers is Variables, Structs {\n function normalSlot(uint256 slot_) public pure returns (bytes32) {\n return bytes32(slot_);\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for single mapping\n function calculateStorageSlotUintMapping(uint256 slot_, uint key_) public pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for single mapping\n function calculateStorageSlotIntMapping(uint256 slot_, int key_) public pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for double mapping\n function calculateDoubleIntUintMapping(uint256 slot_, int key1_, uint key2_) public pure returns (bytes32) {\n bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));\n return keccak256(abi.encode(key2_, intermediateSlot_));\n }\n\n function tickHelper(uint tickRaw_) public pure returns (int tick) {\n require(tickRaw_ < X20, \"invalid-number\");\n if (tickRaw_ > 0) {\n tick = tickRaw_ & 1 == 1 ? int((tickRaw_ >> 1) & X19) : -int((tickRaw_ >> 1) & X19);\n } else {\n tick = type(int).min;\n }\n }\n\n constructor(\n address factory_,\n address liquidity_,\n address liquidityResolver_\n ) Variables(factory_, liquidity_, liquidityResolver_) {}\n}\n" }, "contracts/periphery/resolvers/vault/iVaultResolver.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { Structs } from \"./structs.sol\";\n\ninterface IFluidVaultResolver {\n function vaultByNftId(uint nftId_) external view returns (address vault_);\n\n function positionByNftId(\n uint nftId_\n ) external view returns (Structs.UserPosition memory userPosition_, Structs.VaultEntireData memory vaultData_);\n\n function getVaultVariablesRaw(address vault_) external view returns (uint);\n\n function getVaultVariables2Raw(address vault_) external view returns (uint);\n\n function getTickHasDebtRaw(address vault_, int key_) external view returns (uint);\n\n function getTickDataRaw(address vault_, int tick_) external view returns (uint);\n\n function getBranchDataRaw(address vault_, uint branch_) external view returns (uint);\n\n function getAllVaultsAddresses() external view returns (address[] memory vaults_);\n\n function getVaultLiquidation(\n address vault_,\n uint tokenInAmt_\n ) external returns (Structs.LiquidationStruct memory liquidationData_);\n\n function getVaultEntireData(address vault_) external view returns (Structs.VaultEntireData memory vaultData_);\n}\n" }, "contracts/periphery/resolvers/vault/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Helpers } from \"./helpers.sol\";\nimport { TickMath } from \"../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\nimport { IFluidOracle } from \"../../../oracle/fluidOracle.sol\";\nimport { IFluidVaultT1 } from \"../../../protocols/vault/interfaces/iVaultT1.sol\";\nimport { Structs as FluidLiquidityResolverStructs } from \"../liquidity/structs.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { LiquidityCalcs } from \"../../../libraries/liquidityCalcs.sol\";\n\ninterface TokenInterface {\n function balanceOf(address) external view returns (uint);\n}\n\n/// @notice Fluid Vault protocol resolver\n/// Implements various view-only methods to give easy access to Vault protocol data.\ncontract FluidVaultResolver is Helpers {\n constructor(\n address factory_,\n address liquidity_,\n address liquidityResolver_\n ) Helpers(factory_, liquidity_, liquidityResolver_) {}\n\n function getVaultAddress(uint256 vaultId_) public view returns (address vault_) {\n // @dev based on https://ethereum.stackexchange.com/a/61413\n bytes memory data;\n if (vaultId_ == 0x00) {\n // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment\n return address(0);\n } else if (vaultId_ <= 0x7f) {\n data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), address(FACTORY), uint8(vaultId_));\n } else if (vaultId_ <= 0xff) {\n data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), address(FACTORY), bytes1(0x81), uint8(vaultId_));\n } else if (vaultId_ <= 0xffff) {\n data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), address(FACTORY), bytes1(0x82), uint16(vaultId_));\n } else if (vaultId_ <= 0xffffff) {\n data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), address(FACTORY), bytes1(0x83), uint24(vaultId_));\n } else {\n data = abi.encodePacked(bytes1(0xda), bytes1(0x94), address(FACTORY), bytes1(0x84), uint32(vaultId_));\n }\n\n return address(uint160(uint256(keccak256(data))));\n }\n\n function getVaultId(address vault_) public view returns (uint id_) {\n id_ = IFluidVaultT1(vault_).VAULT_ID();\n }\n\n function getTokenConfig(uint nftId_) public view returns (uint) {\n return FACTORY.readFromStorage(calculateStorageSlotUintMapping(3, nftId_));\n }\n\n function getVaultVariablesRaw(address vault_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(normalSlot(0));\n }\n\n function getVaultVariables2Raw(address vault_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(normalSlot(1));\n }\n\n function getAbsorbedLiquidityRaw(address vault_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(normalSlot(2));\n }\n\n function getPositionDataRaw(address vault_, uint positionId_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateStorageSlotUintMapping(3, positionId_));\n }\n\n // if tick > 0 then key_ = tick / 256\n // if tick < 0 then key_ = (tick / 256) - 1\n function getTickHasDebtRaw(address vault_, int key_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateStorageSlotIntMapping(4, key_));\n }\n\n function getTickDataRaw(address vault_, int tick_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateStorageSlotIntMapping(5, tick_));\n }\n\n // TODO: Verify below\n // id_ = (realId_ / 3) + 1\n function getTickIdDataRaw(address vault_, int tick_, uint id_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateDoubleIntUintMapping(6, tick_, id_));\n }\n\n function getBranchDataRaw(address vault_, uint branch_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateStorageSlotUintMapping(7, branch_));\n }\n\n function getRateRaw(address vault_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(normalSlot(8));\n }\n\n function getRebalancer(address vault_) public view returns (address) {\n return address(uint160(IFluidVaultT1(vault_).readFromStorage(normalSlot(9))));\n }\n\n function getTotalVaults() public view returns (uint) {\n return FACTORY.totalVaults();\n }\n\n function getAllVaultsAddresses() public view returns (address[] memory vaults_) {\n uint totalVaults_ = getTotalVaults();\n vaults_ = new address[](totalVaults_);\n for (uint i = 0; i < totalVaults_; i++) {\n vaults_[i] = getVaultAddress((i + 1));\n }\n }\n\n function _getVaultConstants(address vault_) internal view returns (IFluidVaultT1.ConstantViews memory constants_) {\n constants_ = IFluidVaultT1(vault_).constantsView();\n }\n\n function _getVaultConfig(address vault_) internal view returns (Configs memory configs_) {\n uint vaultVariables2_ = getVaultVariables2Raw(vault_);\n configs_.supplyRateMagnifier = uint16(vaultVariables2_ & X16);\n configs_.borrowRateMagnifier = uint16((vaultVariables2_ >> 16) & X16);\n configs_.collateralFactor = (uint16((vaultVariables2_ >> 32) & X10)) * 10;\n configs_.liquidationThreshold = (uint16((vaultVariables2_ >> 42) & X10)) * 10;\n configs_.liquidationMaxLimit = (uint16((vaultVariables2_ >> 52) & X10) * 10);\n configs_.withdrawalGap = uint16((vaultVariables2_ >> 62) & X10) * 10;\n configs_.liquidationPenalty = uint16((vaultVariables2_ >> 72) & X10);\n configs_.borrowFee = uint16((vaultVariables2_ >> 82) & X10);\n configs_.oracle = address(uint160(vaultVariables2_ >> 96));\n if (configs_.oracle != address(0)) {\n try IFluidOracle(configs_.oracle).getExchangeRateOperate() returns (uint256 exchangeRate_) {\n configs_.oraclePriceOperate = exchangeRate_;\n configs_.oraclePriceLiquidate = IFluidOracle(configs_.oracle).getExchangeRateLiquidate();\n } catch {\n // deprecated backward compatible for older vaults oracles\n configs_.oraclePriceOperate = IFluidOracle(configs_.oracle).getExchangeRate();\n configs_.oraclePriceLiquidate = configs_.oraclePriceOperate;\n }\n }\n configs_.rebalancer = getRebalancer(vault_);\n }\n\n function _getExchangePricesAndRates(\n address vault_,\n Configs memory configs_,\n FluidLiquidityResolverStructs.OverallTokenData memory liquiditySupplytokenData_,\n FluidLiquidityResolverStructs.OverallTokenData memory liquidityBorrowtokenData_\n ) internal view returns (ExchangePricesAndRates memory exchangePricesAndRates_) {\n uint exchangePrices_ = getRateRaw(vault_);\n exchangePricesAndRates_.lastStoredLiquiditySupplyExchangePrice = exchangePrices_ & X64;\n exchangePricesAndRates_.lastStoredLiquidityBorrowExchangePrice = (exchangePrices_ >> 64) & X64;\n exchangePricesAndRates_.lastStoredVaultSupplyExchangePrice = (exchangePrices_ >> 128) & X64;\n exchangePricesAndRates_.lastStoredVaultBorrowExchangePrice = (exchangePrices_ >> 192) & X64;\n\n (\n exchangePricesAndRates_.liquiditySupplyExchangePrice,\n exchangePricesAndRates_.liquidityBorrowExchangePrice,\n exchangePricesAndRates_.vaultSupplyExchangePrice,\n exchangePricesAndRates_.vaultBorrowExchangePrice\n ) = IFluidVaultT1(vault_).updateExchangePrices(getVaultVariables2Raw(vault_));\n\n exchangePricesAndRates_.supplyRateLiquidity = liquiditySupplytokenData_.supplyRate;\n exchangePricesAndRates_.borrowRateLiquidity = liquidityBorrowtokenData_.borrowRate;\n exchangePricesAndRates_.supplyRateVault =\n (liquiditySupplytokenData_.supplyRate * configs_.supplyRateMagnifier) /\n 10000;\n exchangePricesAndRates_.borrowRateVault =\n (liquidityBorrowtokenData_.borrowRate * configs_.borrowRateMagnifier) /\n 10000;\n exchangePricesAndRates_.rewardsRate = configs_.supplyRateMagnifier > 10000\n ? configs_.supplyRateMagnifier - 10000\n : 0;\n }\n\n function _getTotalSupplyAndBorrow(\n address vault_,\n ExchangePricesAndRates memory exchangePricesAndRates_,\n IFluidVaultT1.ConstantViews memory constantsVariables_\n ) internal view returns (TotalSupplyAndBorrow memory totalSupplyAndBorrow_) {\n uint vaultVariables_ = getVaultVariablesRaw(vault_);\n uint absorbedLiquidity_ = getAbsorbedLiquidityRaw(vault_);\n uint totalSupplyLiquidity_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserSupplySlot);\n // extracting user's supply\n totalSupplyLiquidity_ = (totalSupplyLiquidity_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n // converting big number into normal number\n totalSupplyLiquidity_ = (totalSupplyLiquidity_ >> 8) << (totalSupplyLiquidity_ & X8);\n uint totalBorrowLiquidity_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserBorrowSlot);\n // extracting user's borrow\n totalBorrowLiquidity_ = (totalBorrowLiquidity_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64;\n // converting big number into normal number\n totalBorrowLiquidity_ = (totalBorrowLiquidity_ >> 8) << (totalBorrowLiquidity_ & X8);\n\n totalSupplyAndBorrow_.totalSupplyVault = (vaultVariables_ >> 82) & X64;\n // Converting bignumber into normal number\n totalSupplyAndBorrow_.totalSupplyVault =\n (totalSupplyAndBorrow_.totalSupplyVault >> 8) <<\n (totalSupplyAndBorrow_.totalSupplyVault & X8);\n totalSupplyAndBorrow_.totalBorrowVault = (vaultVariables_ >> 146) & X64;\n // Converting bignumber into normal number\n totalSupplyAndBorrow_.totalBorrowVault =\n (totalSupplyAndBorrow_.totalBorrowVault >> 8) <<\n (totalSupplyAndBorrow_.totalBorrowVault & X8);\n\n totalSupplyAndBorrow_.totalSupplyLiquidity = totalSupplyLiquidity_;\n totalSupplyAndBorrow_.totalBorrowLiquidity = totalBorrowLiquidity_;\n\n totalSupplyAndBorrow_.absorbedBorrow = absorbedLiquidity_ & X128;\n totalSupplyAndBorrow_.absorbedSupply = absorbedLiquidity_ >> 128;\n\n // converting raw total supply & total borrow into normal amounts\n totalSupplyAndBorrow_.totalSupplyVault =\n (totalSupplyAndBorrow_.totalSupplyVault * exchangePricesAndRates_.vaultSupplyExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.totalBorrowVault =\n (totalSupplyAndBorrow_.totalBorrowVault * exchangePricesAndRates_.vaultBorrowExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.totalSupplyLiquidity =\n (totalSupplyAndBorrow_.totalSupplyLiquidity * exchangePricesAndRates_.liquiditySupplyExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.totalBorrowLiquidity =\n (totalSupplyAndBorrow_.totalBorrowLiquidity * exchangePricesAndRates_.liquidityBorrowExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.absorbedSupply =\n (totalSupplyAndBorrow_.absorbedSupply * exchangePricesAndRates_.vaultSupplyExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.absorbedBorrow =\n (totalSupplyAndBorrow_.absorbedBorrow * exchangePricesAndRates_.vaultBorrowExchangePrice) /\n 1e12;\n }\n\n function _getLimitsAndAvailability(\n TotalSupplyAndBorrow memory totalSupplyAndBorrow_,\n ExchangePricesAndRates memory exchangePricesAndRates_,\n IFluidVaultT1.ConstantViews memory constantsVariables_,\n Configs memory configs_,\n FluidLiquidityResolverStructs.UserBorrowData memory liquidityBorrowtokenData_\n ) internal view returns (LimitsAndAvailability memory limitsAndAvailability_) {\n // fetching user's supply slot data\n uint userSupplyLiquidityData_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserSupplySlot);\n if (userSupplyLiquidityData_ > 0) {\n // converting current user's supply from big number to normal\n uint userSupply_ = (userSupplyLiquidityData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n userSupply_ = (userSupply_ >> 8) << (userSupply_ & X8);\n\n // fetching liquidity's withdrawal limit\n uint supplyLimitRaw_ = LiquidityCalcs.calcWithdrawalLimitBeforeOperate(\n userSupplyLiquidityData_,\n userSupply_\n );\n\n limitsAndAvailability_.withdrawLimit =\n (supplyLimitRaw_ * exchangePricesAndRates_.liquiditySupplyExchangePrice) /\n 1e12;\n\n limitsAndAvailability_.withdrawableUntilLimit = totalSupplyAndBorrow_.totalSupplyLiquidity >\n limitsAndAvailability_.withdrawLimit\n ? totalSupplyAndBorrow_.totalSupplyLiquidity - limitsAndAvailability_.withdrawLimit\n : 0;\n uint withdrawalGap_ = (totalSupplyAndBorrow_.totalSupplyLiquidity * configs_.withdrawalGap) / 1e4;\n limitsAndAvailability_.withdrawableUntilLimit = (limitsAndAvailability_.withdrawableUntilLimit >\n withdrawalGap_)\n ? (((limitsAndAvailability_.withdrawableUntilLimit - withdrawalGap_) * 999999) / 1000000)\n : 0;\n\n uint balanceOf_;\n if (constantsVariables_.supplyToken == NATIVE_TOKEN_ADDRESS) {\n balanceOf_ = address(LIQUIDITY).balance;\n } else {\n balanceOf_ = TokenInterface(constantsVariables_.supplyToken).balanceOf(address(LIQUIDITY));\n }\n limitsAndAvailability_.withdrawable = balanceOf_ > limitsAndAvailability_.withdrawableUntilLimit\n ? limitsAndAvailability_.withdrawableUntilLimit\n : balanceOf_;\n }\n\n uint userBorrowLiquidityData_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserBorrowSlot);\n if (userBorrowLiquidityData_ > 0) {\n // converting current user's supply from big number to normal\n uint userBorrow_ = (userBorrowLiquidityData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64;\n userBorrow_ = (userBorrow_ >> 8) << (userBorrow_ & X8);\n\n limitsAndAvailability_.borrowLimit = liquidityBorrowtokenData_.borrowLimit;\n limitsAndAvailability_.borrowLimitUtilization = liquidityBorrowtokenData_.borrowLimitUtilization;\n limitsAndAvailability_.borrowableUntilLimit =\n (liquidityBorrowtokenData_.borrowableUntilLimit * 999999) /\n 1000000;\n\n uint balanceOf_;\n if (constantsVariables_.borrowToken == NATIVE_TOKEN_ADDRESS) {\n balanceOf_ = address(LIQUIDITY).balance;\n } else {\n balanceOf_ = TokenInterface(constantsVariables_.borrowToken).balanceOf(address(LIQUIDITY));\n }\n limitsAndAvailability_.borrowable = balanceOf_ > limitsAndAvailability_.borrowableUntilLimit\n ? limitsAndAvailability_.borrowableUntilLimit\n : balanceOf_;\n }\n\n limitsAndAvailability_.minimumBorrowing = (10001 * exchangePricesAndRates_.vaultBorrowExchangePrice) / 1e12;\n }\n\n function getVaultState(address vault_) public view returns (VaultState memory vaultState_) {\n uint vaultVariables_ = getVaultVariablesRaw(vault_);\n\n vaultState_.topTick = tickHelper(((vaultVariables_ >> 2) & X20));\n vaultState_.currentBranch = (vaultVariables_ >> 22) & X30;\n vaultState_.totalBranch = (vaultVariables_ >> 52) & X30;\n vaultState_.totalSupply = BigMathMinified.fromBigNumber((vaultVariables_ >> 82) & X64, 8, X8);\n vaultState_.totalBorrow = BigMathMinified.fromBigNumber((vaultVariables_ >> 146) & X64, 8, X8);\n vaultState_.totalPositions = (vaultVariables_ >> 210) & X32;\n\n uint currentBranchData_ = getBranchDataRaw(vault_, vaultState_.currentBranch);\n vaultState_.currentBranchState.status = currentBranchData_ & 3;\n vaultState_.currentBranchState.minimaTick = tickHelper(((currentBranchData_ >> 2) & X20));\n vaultState_.currentBranchState.debtFactor = (currentBranchData_ >> 116) & X50;\n vaultState_.currentBranchState.partials = (currentBranchData_ >> 22) & X30;\n vaultState_.currentBranchState.debtLiquidity = BigMathMinified.fromBigNumber(\n (currentBranchData_ >> 52) & X64,\n 8,\n X8\n );\n vaultState_.currentBranchState.baseBranchId = (currentBranchData_ >> 166) & X30;\n vaultState_.currentBranchState.baseBranchMinima = tickHelper(((currentBranchData_ >> 196) & X20));\n }\n\n function getVaultEntireData(address vault_) public view returns (VaultEntireData memory vaultData_) {\n vaultData_.vault = vault_;\n vaultData_.constantVariables = _getVaultConstants(vault_);\n\n (\n FluidLiquidityResolverStructs.UserSupplyData memory liquidityUserSupplyData_,\n FluidLiquidityResolverStructs.OverallTokenData memory liquiditySupplyTokenData_\n ) = LIQUIDITY_RESOLVER.getUserSupplyData(vault_, vaultData_.constantVariables.supplyToken);\n\n (\n FluidLiquidityResolverStructs.UserBorrowData memory liquidityUserBorrowData_,\n FluidLiquidityResolverStructs.OverallTokenData memory liquidityBorrowTokenData_\n ) = LIQUIDITY_RESOLVER.getUserBorrowData(vault_, vaultData_.constantVariables.borrowToken);\n\n vaultData_.configs = _getVaultConfig(vault_);\n vaultData_.exchangePricesAndRates = _getExchangePricesAndRates(\n vault_,\n vaultData_.configs,\n liquiditySupplyTokenData_,\n liquidityBorrowTokenData_\n );\n vaultData_.totalSupplyAndBorrow = _getTotalSupplyAndBorrow(\n vault_,\n vaultData_.exchangePricesAndRates,\n vaultData_.constantVariables\n );\n vaultData_.limitsAndAvailability = _getLimitsAndAvailability(\n vaultData_.totalSupplyAndBorrow,\n vaultData_.exchangePricesAndRates,\n vaultData_.constantVariables,\n vaultData_.configs,\n liquidityUserBorrowData_\n );\n vaultData_.vaultState = getVaultState(vault_);\n\n vaultData_.liquidityUserSupplyData = liquidityUserSupplyData_;\n vaultData_.liquidityUserBorrowData = liquidityUserBorrowData_;\n }\n\n function getVaultsEntireData(\n address[] memory vaults_\n ) external view returns (VaultEntireData[] memory vaultsData_) {\n uint length_ = vaults_.length;\n vaultsData_ = new VaultEntireData[](length_);\n for (uint i = 0; i < length_; i++) {\n vaultsData_[i] = getVaultEntireData(vaults_[i]);\n }\n }\n\n function getVaultsEntireData() external view returns (VaultEntireData[] memory vaultsData_) {\n address[] memory vaults_ = getAllVaultsAddresses();\n uint length_ = vaults_.length;\n vaultsData_ = new VaultEntireData[](length_);\n for (uint i = 0; i < length_; i++) {\n vaultsData_[i] = getVaultEntireData(vaults_[i]);\n }\n }\n\n function positionByNftId(\n uint nftId_\n ) public view returns (UserPosition memory userPosition_, VaultEntireData memory vaultData_) {\n userPosition_.nftId = nftId_;\n address vault_ = vaultByNftId(nftId_);\n if (vault_ != address(0)) {\n uint positionData_ = getPositionDataRaw(vault_, nftId_);\n vaultData_ = getVaultEntireData(vault_);\n\n userPosition_.owner = FACTORY.ownerOf(nftId_);\n userPosition_.isSupplyPosition = (positionData_ & 1) == 1;\n userPosition_.supply = (positionData_ >> 45) & X64;\n // Converting big number into normal number\n userPosition_.supply = (userPosition_.supply >> 8) << (userPosition_.supply & X8);\n userPosition_.beforeSupply = userPosition_.supply;\n userPosition_.dustBorrow = (positionData_ >> 109) & X64;\n // Converting big number into normal number\n userPosition_.dustBorrow = (userPosition_.dustBorrow >> 8) << (userPosition_.dustBorrow & X8);\n userPosition_.beforeDustBorrow = userPosition_.dustBorrow;\n if (!userPosition_.isSupplyPosition) {\n userPosition_.tick = (positionData_ & 2) == 2\n ? int((positionData_ >> 2) & X19)\n : -int((positionData_ >> 2) & X19);\n userPosition_.tickId = (positionData_ >> 21) & X24;\n userPosition_.borrow =\n (TickMath.getRatioAtTick(int24(userPosition_.tick)) * userPosition_.supply) >>\n 96;\n userPosition_.beforeBorrow = userPosition_.borrow - userPosition_.beforeDustBorrow;\n\n uint tickData_ = getTickDataRaw(vault_, userPosition_.tick);\n\n if (((tickData_ & 1) == 1) || (((tickData_ >> 1) & X24) > userPosition_.tickId)) {\n // user got liquidated\n userPosition_.isLiquidated = true;\n (userPosition_.tick, userPosition_.borrow, userPosition_.supply, , ) = IFluidVaultT1(vault_)\n .fetchLatestPosition(userPosition_.tick, userPosition_.tickId, userPosition_.borrow, tickData_);\n }\n\n if (userPosition_.borrow > userPosition_.dustBorrow) {\n userPosition_.borrow = userPosition_.borrow - userPosition_.dustBorrow;\n } else {\n // TODO: Make sure this is right. If borrow is less than dust debt then both gets 0\n userPosition_.borrow = 0;\n userPosition_.dustBorrow = 0;\n }\n }\n\n // converting raw amounts into normal\n userPosition_.beforeSupply =\n (userPosition_.beforeSupply * vaultData_.exchangePricesAndRates.vaultSupplyExchangePrice) /\n 1e12;\n userPosition_.beforeBorrow =\n (userPosition_.beforeBorrow * vaultData_.exchangePricesAndRates.vaultBorrowExchangePrice) /\n 1e12;\n userPosition_.beforeDustBorrow =\n (userPosition_.beforeDustBorrow * vaultData_.exchangePricesAndRates.vaultBorrowExchangePrice) /\n 1e12;\n userPosition_.supply =\n (userPosition_.supply * vaultData_.exchangePricesAndRates.vaultSupplyExchangePrice) /\n 1e12;\n userPosition_.borrow =\n (userPosition_.borrow * vaultData_.exchangePricesAndRates.vaultBorrowExchangePrice) /\n 1e12;\n userPosition_.dustBorrow =\n (userPosition_.dustBorrow * vaultData_.exchangePricesAndRates.vaultBorrowExchangePrice) /\n 1e12;\n }\n }\n\n function positionsNftIdOfUser(address user_) public view returns (uint[] memory nftIds_) {\n uint totalPositions_ = FACTORY.balanceOf(user_);\n nftIds_ = new uint[](totalPositions_);\n for (uint i; i < totalPositions_; i++) {\n nftIds_[i] = FACTORY.tokenOfOwnerByIndex(user_, i);\n }\n }\n\n function vaultByNftId(uint nftId_) public view returns (address vault_) {\n uint tokenConfig_ = getTokenConfig(nftId_);\n vault_ = FACTORY.getVaultAddress((tokenConfig_ >> 192) & X32);\n }\n\n function positionsByUser(\n address user_\n ) external view returns (UserPosition[] memory userPositions_, VaultEntireData[] memory vaultsData_) {\n uint[] memory nftIds_ = positionsNftIdOfUser(user_);\n uint length_ = nftIds_.length;\n userPositions_ = new UserPosition[](length_);\n vaultsData_ = new VaultEntireData[](length_);\n for (uint i = 0; i < length_; i++) {\n (userPositions_[i], vaultsData_[i]) = positionByNftId(nftIds_[i]);\n }\n }\n\n function totalPositions() external view returns (uint) {\n return FACTORY.totalSupply();\n }\n\n /// @dev fetches available liquidations\n /// @param vault_ address of vault for which to fetch\n /// @param tokenInAmt_ token in aka debt to payback, leave 0 to get max\n /// @return liquidationData_ liquidation related data. Check out structs.sol\n function getVaultLiquidation(\n address vault_,\n uint tokenInAmt_\n ) public returns (LiquidationStruct memory liquidationData_) {\n liquidationData_.vault = vault_;\n IFluidVaultT1.ConstantViews memory constants_ = _getVaultConstants(vault_);\n liquidationData_.tokenIn = constants_.borrowToken;\n liquidationData_.tokenOut = constants_.supplyToken;\n\n uint amtOut_;\n uint amtIn_;\n\n tokenInAmt_ = tokenInAmt_ == 0 ? X128 : tokenInAmt_;\n // running without absorb\n try IFluidVaultT1(vault_).liquidate(tokenInAmt_, 0, 0x000000000000000000000000000000000000dEaD, false) {\n // Handle successful execution\n } catch Error(string memory) {\n // Handle generic errors with a reason\n } catch (bytes memory lowLevelData_) {\n // Check if the error data is long enough to contain a selector\n if (lowLevelData_.length >= 68) {\n bytes4 errorSelector_;\n assembly {\n // Extract the selector from the error data\n errorSelector_ := mload(add(lowLevelData_, 0x20))\n }\n if (errorSelector_ == IFluidVaultT1.FluidLiquidateResult.selector) {\n assembly {\n amtOut_ := mload(add(lowLevelData_, 36))\n amtIn_ := mload(add(lowLevelData_, 68))\n }\n liquidationData_.tokenOutAmtOne = amtOut_;\n liquidationData_.tokenInAmtOne = amtIn_;\n } else {\n // tokenInAmtOne & tokenOutAmtOne remains 0\n }\n }\n }\n\n // running with absorb\n try IFluidVaultT1(vault_).liquidate(tokenInAmt_, 0, 0x000000000000000000000000000000000000dEaD, true) {\n // Handle successful execution\n } catch Error(string memory) {\n // Handle generic errors with a reason\n } catch (bytes memory lowLevelData_) {\n // Check if the error data is long enough to contain a selector\n if (lowLevelData_.length >= 68) {\n bytes4 errorSelector_;\n assembly {\n // Extract the selector from the error data\n errorSelector_ := mload(add(lowLevelData_, 0x20))\n }\n if (errorSelector_ == IFluidVaultT1.FluidLiquidateResult.selector) {\n assembly {\n amtOut_ := mload(add(lowLevelData_, 36))\n amtIn_ := mload(add(lowLevelData_, 68))\n }\n liquidationData_.tokenOutAmtTwo = amtOut_;\n liquidationData_.tokenInAmtTwo = amtIn_;\n } else {\n // tokenInAmtTwo & tokenOutAmtTwo remains 0\n }\n }\n }\n }\n\n function getMultipleVaultsLiquidation(\n address[] memory vaults_,\n uint[] memory tokensInAmt_\n ) external returns (LiquidationStruct[] memory liquidationsData_) {\n uint length_ = vaults_.length;\n liquidationsData_ = new LiquidationStruct[](length_);\n for (uint i = 0; i < length_; i++) {\n liquidationsData_[i] = getVaultLiquidation(vaults_[i], tokensInAmt_[i]);\n }\n }\n\n function getAllVaultsLiquidation() external returns (LiquidationStruct[] memory liquidationsData_) {\n address[] memory vaults_ = getAllVaultsAddresses();\n uint length_ = vaults_.length;\n\n liquidationsData_ = new LiquidationStruct[](length_);\n for (uint i = 0; i < length_; i++) {\n liquidationsData_[i] = getVaultLiquidation(vaults_[i], 0);\n }\n }\n\n function getVaultAbsorb(address vault_) public returns (AbsorbStruct memory absorbData_) {\n absorbData_.vault = vault_;\n uint absorbedLiquidity_ = getAbsorbedLiquidityRaw(vault_);\n try IFluidVaultT1(vault_).absorb() {\n // Handle successful execution\n uint newAbsorbedLiquidity_ = getAbsorbedLiquidityRaw(vault_);\n if (newAbsorbedLiquidity_ != absorbedLiquidity_) {\n absorbData_.absorbAvailable = true;\n }\n } catch Error(string memory) {} catch (bytes memory) {}\n }\n\n function getVaultsAbsorb(address[] memory vaults_) public returns (AbsorbStruct[] memory absorbData_) {\n uint length_ = vaults_.length;\n absorbData_ = new AbsorbStruct[](length_);\n for (uint i = 0; i < length_; i++) {\n absorbData_[i] = getVaultAbsorb(vaults_[i]);\n }\n }\n\n function getVaultsAbsorb() public returns (AbsorbStruct[] memory absorbData_) {\n return getVaultsAbsorb(getAllVaultsAddresses());\n }\n}\n" }, "contracts/periphery/resolvers/vault/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidVaultT1 } from \"../../../protocols/vault/interfaces/iVaultT1.sol\";\nimport { Structs as FluidLiquidityResolverStructs } from \"../liquidity/structs.sol\";\n\ncontract Structs {\n struct Configs {\n uint16 supplyRateMagnifier;\n uint16 borrowRateMagnifier;\n uint16 collateralFactor;\n uint16 liquidationThreshold;\n uint16 liquidationMaxLimit;\n uint16 withdrawalGap;\n uint16 liquidationPenalty;\n uint16 borrowFee;\n address oracle;\n uint oraclePriceOperate;\n uint oraclePriceLiquidate;\n address rebalancer;\n }\n\n struct ExchangePricesAndRates {\n uint lastStoredLiquiditySupplyExchangePrice;\n uint lastStoredLiquidityBorrowExchangePrice;\n uint lastStoredVaultSupplyExchangePrice;\n uint lastStoredVaultBorrowExchangePrice;\n uint liquiditySupplyExchangePrice;\n uint liquidityBorrowExchangePrice;\n uint vaultSupplyExchangePrice;\n uint vaultBorrowExchangePrice;\n uint supplyRateVault;\n uint borrowRateVault;\n uint supplyRateLiquidity;\n uint borrowRateLiquidity;\n uint rewardsRate; // rewards rate in percent 1e2 precision (1% = 100, 100% = 10000)\n }\n\n struct TotalSupplyAndBorrow {\n uint totalSupplyVault;\n uint totalBorrowVault;\n uint totalSupplyLiquidity;\n uint totalBorrowLiquidity;\n uint absorbedSupply;\n uint absorbedBorrow;\n }\n\n struct LimitsAndAvailability {\n uint withdrawLimit;\n uint withdrawableUntilLimit;\n uint withdrawable;\n uint borrowLimit;\n uint borrowableUntilLimit; // borrowable amount until any borrow limit (incl. max utilization limit)\n uint borrowable; // actual currently borrowable amount (borrow limit - already borrowed) & considering balance, max utilization\n uint borrowLimitUtilization; // borrow limit for `maxUtilization` config at Liquidity\n uint minimumBorrowing;\n }\n\n struct CurrentBranchState {\n uint status; // if 0 then not liquidated, if 1 then liquidated, if 2 then merged, if 3 then closed\n int minimaTick;\n uint debtFactor;\n uint partials;\n uint debtLiquidity;\n uint baseBranchId;\n int baseBranchMinima;\n }\n\n struct VaultState {\n uint totalPositions;\n int topTick;\n uint currentBranch;\n uint totalBranch;\n uint totalBorrow;\n uint totalSupply;\n CurrentBranchState currentBranchState;\n }\n\n struct VaultEntireData {\n address vault;\n IFluidVaultT1.ConstantViews constantVariables;\n Configs configs;\n ExchangePricesAndRates exchangePricesAndRates;\n TotalSupplyAndBorrow totalSupplyAndBorrow;\n LimitsAndAvailability limitsAndAvailability;\n VaultState vaultState;\n // liquidity related data such as supply amount, limits, expansion etc.\n FluidLiquidityResolverStructs.UserSupplyData liquidityUserSupplyData;\n // liquidity related data such as borrow amount, limits, expansion etc.\n FluidLiquidityResolverStructs.UserBorrowData liquidityUserBorrowData;\n }\n\n struct UserPosition {\n uint nftId;\n address owner;\n bool isLiquidated;\n bool isSupplyPosition; // if true that means borrowing is 0\n int tick;\n uint tickId;\n uint beforeSupply;\n uint beforeBorrow;\n uint beforeDustBorrow;\n uint supply;\n uint borrow;\n uint dustBorrow;\n }\n\n /// @dev liquidation related data\n /// @param vault address of vault\n /// @param tokenIn_ address of token in\n /// @param tokenOut_ address of token out\n /// @param tokenInAmtOne_ (without absorb liquidity) minimum of available liquidation & tokenInAmt_\n /// @param tokenOutAmtOne_ (without absorb liquidity) expected token out, collateral to withdraw\n /// @param tokenInAmtTwo_ (absorb liquidity included) minimum of available liquidation & tokenInAmt_. In most cases it'll be same as tokenInAmtOne_ but sometimes can be bigger.\n /// @param tokenOutAmtTwo_ (absorb liquidity included) expected token out, collateral to withdraw. In most cases it'll be same as tokenOutAmtOne_ but sometimes can be bigger.\n /// @dev Liquidity in Two will always be >= One. Sometimes One can provide better swaps, sometimes Two can provide better swaps. But available in Two will always be >= One\n struct LiquidationStruct {\n address vault;\n address tokenIn;\n address tokenOut;\n uint tokenInAmtOne;\n uint tokenOutAmtOne;\n uint tokenInAmtTwo;\n uint tokenOutAmtTwo;\n }\n\n struct AbsorbStruct {\n address vault;\n bool absorbAvailable;\n }\n}\n" }, "contracts/periphery/resolvers/vault/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidityResolver } from \"../liquidity/iLiquidityResolver.sol\";\nimport { IFluidVaultFactory } from \"../../../protocols/vault/interfaces/iVaultFactory.sol\";\n\ninterface IFluidLiquidity {\n function readFromStorage(bytes32 slot_) external view returns (uint256 result_);\n}\n\ncontract Variables {\n IFluidVaultFactory public immutable FACTORY;\n IFluidLiquidity public immutable LIQUIDITY;\n IFluidLiquidityResolver public immutable LIQUIDITY_RESOLVER;\n\n // 30 bits (used for partials mainly)\n uint internal constant X8 = 0xff;\n uint internal constant X10 = 0x3ff;\n uint internal constant X14 = 0x3fff;\n uint internal constant X15 = 0x7fff;\n uint internal constant X16 = 0xffff;\n uint internal constant X19 = 0x7ffff;\n uint internal constant X20 = 0xfffff;\n uint internal constant X24 = 0xffffff;\n uint internal constant X25 = 0x1ffffff;\n uint internal constant X30 = 0x3fffffff;\n uint internal constant X32 = 0xffffffff;\n uint internal constant X35 = 0x7ffffffff;\n uint internal constant X40 = 0xffffffffff;\n uint internal constant X50 = 0x3ffffffffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n uint internal constant X96 = 0xffffffffffffffffffffffff;\n uint internal constant X128 = 0xffffffffffffffffffffffffffffffff;\n /// @dev address that is mapped to the chain native token\n address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n constructor(address factory_, address liquidity_, address liquidityResolver_) {\n FACTORY = IFluidVaultFactory(factory_);\n LIQUIDITY = IFluidLiquidity(liquidity_);\n LIQUIDITY_RESOLVER = IFluidLiquidityResolver(liquidityResolver_);\n }\n}\n" }, "contracts/periphery/resolvers/vaultLiquidation/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"./variables.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { Structs as VaultResolverStructs } from \"../vault/structs.sol\";\nimport { IFluidVaultResolver } from \"../vault/iVaultResolver.sol\";\nimport { IFluidVaultT1 } from \"../../../protocols/vault/interfaces/iVaultT1.sol\";\n\n/// @notice Resolver contract that helps in finding available token swaps through Fluid Vault liquidations.\ncontract FluidVaultLiquidationResolver is Variables, Structs {\n /// @notice thrown if an input param address is zero\n error FluidVaultLiquidationsResolver__AddressZero();\n /// @notice thrown if an invalid param is given to a method\n error FluidVaultLiquidationsResolver__InvalidParams();\n\n /// @notice constructor sets the immutable vault resolver address\n constructor(IFluidVaultResolver vaultResolver_) Variables(vaultResolver_) {\n if (address(vaultResolver_) == address(0)) {\n revert FluidVaultLiquidationsResolver__AddressZero();\n }\n }\n\n /// @notice returns all token swap pairs available through Fluid Vault Liquidations\n function getAllSwapPairs() public view returns (VaultData[] memory vaultDatas_) {\n address[] memory vaultAddresses_ = VAULT_RESOLVER.getAllVaultsAddresses();\n vaultDatas_ = new VaultData[](vaultAddresses_.length);\n\n IFluidVaultT1.ConstantViews memory constants_;\n for (uint256 i; i < vaultAddresses_.length; ++i) {\n constants_ = IFluidVaultT1(vaultAddresses_[i]).constantsView();\n vaultDatas_[i] = VaultData({\n vault: vaultAddresses_[i],\n tokenIn: constants_.borrowToken,\n tokenOut: constants_.supplyToken\n });\n }\n }\n\n /// @notice returns the vault address for a certain `tokenIn_` swapped to a `tokenOut_`.\n /// returns zero address if no vault is available for a given pair.\n /// @dev for native token, send 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE.\n function getVaultForSwap(address tokenIn_, address tokenOut_) public view returns (address vault_) {\n address[] memory vaults_ = VAULT_RESOLVER.getAllVaultsAddresses();\n\n IFluidVaultT1.ConstantViews memory constants_;\n for (uint256 i; i < vaults_.length; ++i) {\n constants_ = IFluidVaultT1(vaults_[i]).constantsView();\n\n if (constants_.borrowToken == tokenIn_ && constants_.supplyToken == tokenOut_) {\n return vaults_[i];\n }\n }\n }\n\n /// @notice returns all available token pair swaps for any `tokensIn_` to any `tokensOut_` with the vault address.\n /// @dev for native token, send 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE.\n function getVaultsForSwap(\n address[] calldata tokensIn_,\n address[] calldata tokensOut_\n ) public view returns (VaultData[] memory vaultDatas_) {\n uint256 maxCombinations_ = tokensIn_.length * tokensOut_.length;\n\n VaultData[] memory allVaults_ = new VaultData[](maxCombinations_);\n\n address[] memory vaultAddresses_ = VAULT_RESOLVER.getAllVaultsAddresses();\n\n uint256 matches_;\n uint256 index_;\n\n IFluidVaultT1.ConstantViews memory constants_;\n for (uint256 vi; vi < vaultAddresses_.length; ++vi) {\n constants_ = IFluidVaultT1(vaultAddresses_[vi]).constantsView();\n\n index_ = 0;\n // for each vault, iterate over all possible input params token combinations\n for (uint256 i; i < tokensIn_.length; ++i) {\n for (uint256 j; j < tokensOut_.length; ++j) {\n if (constants_.borrowToken == tokensIn_[i] && constants_.supplyToken == tokensOut_[j]) {\n allVaults_[index_] = VaultData({\n vault: vaultAddresses_[vi],\n tokenIn: tokensIn_[i],\n tokenOut: tokensOut_[j]\n });\n ++matches_;\n }\n ++index_;\n }\n }\n }\n\n vaultDatas_ = new VaultData[](matches_);\n index_ = 0;\n for (uint256 i; i < maxCombinations_; ++i) {\n if (allVaults_[i].vault != address(0)) {\n vaultDatas_[index_] = allVaults_[i];\n ++index_;\n }\n }\n }\n\n /// @notice finds the total available swappable amount for a `tokenIn_` to `tokenOut_` swap, considering both a swap\n /// that uses liquidation with absorb and without absorb. Sometimes with absorb can provide better swaps,\n /// sometimes without absorb can provide better swaps. But available liquidity for \"withAbsorb\" amounts will\n /// always be >= normal amounts.\n /// @dev returned data can be fed into `getSwapCalldata` to prepare the tx that executes the swap.\n /// @dev expected to be called with callStatic, although this method does not do any actual state changes anyway.\n /// @dev for native token, send 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE.\n function getSwapAvailable(address tokenIn_, address tokenOut_) public returns (SwapData memory swapData_) {\n return getSwapDataForVault(getVaultForSwap(tokenIn_, tokenOut_));\n }\n\n /// @notice finds the total available swappable amount for any `tokensIn_` to any `tokesnOut_` swap, considering both\n /// a swap that uses liquidation with absorb and without absorb. Sometimes with absorb can provide better swaps,\n /// sometimes without absorb can provide better swaps. But available liquidity for \"withAbsorb\" amounts will\n /// always be >= normal amounts. Token pairs that are not available will not be listed in returned SwapData array.\n /// e.g. for tokensIn_: USDC & USDT and tokensOut_: ETH & wstETH, this would return any available token pair incl.\n /// the available swappable amounts, so for USDC -> ETH, USDC -> wstETH, USDT -> ETH, USDT -> wstETH.\n /// @dev returned data can be fed into `getSwapCalldata` to prepare the tx that executes the swap.\n /// @dev expected to be called with callStatic, although this method does not do any actual state changes anyway.\n /// @dev for native token, send 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE.\n function getSwapsAvailable(\n address[] calldata tokensIn_,\n address[] calldata tokensOut_\n ) public returns (SwapData[] memory swapDatas_) {\n VaultData[] memory vaults_ = getVaultsForSwap(tokensIn_, tokensOut_);\n\n swapDatas_ = new SwapData[](vaults_.length);\n\n for (uint256 i; i < vaults_.length; ++i) {\n swapDatas_[i] = getSwapDataForVault(vaults_[i].vault);\n }\n }\n\n /// @notice returns the calldata to execute a swap as found through this contract by triggering a vault liquidation.\n /// `tokenInAmt_` must come from msg.sender, `tokenOutAmt_` goes to `receiver_`. If the input token is the\n /// native token, msg.value must be sent along when triggering the actual call with the returned calldata.\n /// @param vault_ vault address at which the liquidation is executed\n /// @param receiver_ receiver address that the output token is sent to\n /// @param tokenInAmt_ input token amount (debt token at vault)\n /// @param tokenOutAmt_ expected output token amount (collateral token at vault)\n /// @param slippage_ maximum allowed slippage for the expected output token amount. Reverts iIf received token out\n /// amount is lower than this. in 1e4 percentage, e.g. 1% = 10000, 0.3% = 3000, 0.01% = 100, 0.0001% = 1.\n /// @param withAbsorb_ set to true to trigger liquidation with executing `absorb()` first. Liquidity is >= when this\n /// is set to true. Rate can be better with or without, check before via other methods.\n /// @return calldata_ the calldata that can be used to trigger the liquidation call, resulting in the desired swap.\n function getSwapCalldata(\n address vault_,\n address receiver_,\n uint256 tokenInAmt_,\n uint256 tokenOutAmt_,\n uint256 slippage_,\n bool withAbsorb_\n ) public pure returns (bytes memory calldata_) {\n if (vault_ == address(0) || receiver_ == address(0)) {\n revert FluidVaultLiquidationsResolver__AddressZero();\n }\n if (slippage_ >= 1e6 || tokenInAmt_ == 0 || tokenOutAmt_ == 0) {\n revert FluidVaultLiquidationsResolver__InvalidParams();\n }\n\n uint256 colPerUnitDebt_ = (tokenOutAmt_ * 1e18) / tokenInAmt_;\n colPerUnitDebt_ = (colPerUnitDebt_ * (1e6 - slippage_)) / 1e6; // e.g. 50 * 99% / 100% = 49.5\n\n calldata_ = abi.encodeWithSelector(\n IFluidVaultT1(vault_).liquidate.selector,\n tokenInAmt_,\n colPerUnitDebt_,\n receiver_,\n withAbsorb_\n );\n }\n\n /// @notice returns the available swap (liquidation) amounts at a certain `vault_`, considering both\n /// a swap that uses liquidation with absorb and without absorb. Sometimes with absorb can provide better swaps,\n /// sometimes without absorb can provide better swaps. But available liquidity for \"withAbsorb\" amounts will\n /// always be >= normal amounts.\n /// @dev returned data can be fed into `getSwapCalldata` to prepare the tx that executes the swap.\n /// @dev expected to be called with callStatic, although this method does not do any actual state changes anyway.\n function getSwapDataForVault(address vault_) public returns (SwapData memory swapData_) {\n if (vault_ == address(0)) {\n return swapData_;\n }\n\n VaultResolverStructs.LiquidationStruct memory liquidationData_ = VAULT_RESOLVER.getVaultLiquidation(vault_, 0);\n swapData_.vault = vault_;\n swapData_.inAmt = liquidationData_.tokenInAmtOne;\n swapData_.outAmt = liquidationData_.tokenOutAmtOne;\n swapData_.inAmtWithAbsorb = liquidationData_.tokenInAmtTwo;\n swapData_.outAmtWithAbsorb = liquidationData_.tokenOutAmtTwo;\n }\n\n /// @notice finds a swap from `tokenIn_` to `tokenOut_` for an exact input amount `inAmt_`. If available amount is\n /// less then the desired input amount, it returns the available amount. Considers the best rate available\n /// for mode with absorb and mode without absorb.\n /// @dev returned data can be fed into `getSwapCalldata` to prepare the tx that executes the swap.\n /// @param tokenIn_ input token (debt token at vault)\n /// @param tokenOut_ output token (collateral token at vault)\n /// @param inAmt_ exact input token amount that should be swapped to output token\n /// @return vault_ vault address at which the swap is available.\n /// @return actualInAmt_ actual input token amount. Equals `inAmt_`, but if less then the desired swap amount is\n /// available, then the available amount is returned instead.\n /// @return outAmt_ received output token amount for `actualInAmt_` of input token\n /// @return withAbsorb_ flag for using mode \"withAbsorb\". Is set to true if a) liquidity without absorb would not\n /// cover the desired `inAmt_` or if b) the rate of with absorb is better than without absorb.\n function exactInput(\n address tokenIn_,\n address tokenOut_,\n uint256 inAmt_\n ) public returns (address vault_, uint256 actualInAmt_, uint256 outAmt_, bool withAbsorb_) {\n SwapData memory swapData_ = getSwapAvailable(tokenIn_, tokenOut_);\n vault_ = swapData_.vault;\n\n actualInAmt_ = inAmt_; // assume inAmt_ can be covered by available amount, var is updated otherwise\n\n uint256 withAbsorbRatio_ = (swapData_.outAmtWithAbsorb * 1e27) / swapData_.inAmtWithAbsorb;\n if (inAmt_ > swapData_.inAmt && swapData_.inAmtWithAbsorb > swapData_.inAmt) {\n // with absorb has more liquidity \n withAbsorb_ = true;\n if (inAmt_ > swapData_.inAmtWithAbsorb) {\n actualInAmt_ = swapData_.inAmtWithAbsorb; // can not cover full requested inAmt_, so set to available\n outAmt_ = swapData_.outAmtWithAbsorb;\n } else {\n // inAmt_ fully covered by with absorb liquidation, get out amount\n outAmt_ = (inAmt_ * withAbsorbRatio_) / 1e27;\n }\n } else {\n // inAmt_ is covered by available liquidation with or without absorb, check which one has better ratio\n uint256 withoutAbsorbRatio_ = (swapData_.outAmt * 1e27) / swapData_.inAmt;\n if (withAbsorbRatio_ > withoutAbsorbRatio_) {\n withAbsorb_ = true;\n outAmt_ = (inAmt_ * withAbsorbRatio_) / 1e27;\n } else {\n outAmt_ = (inAmt_ * withoutAbsorbRatio_) / 1e27;\n }\n }\n }\n\n /// @notice finds a swap from `tokenIn_` to `tokenOut_` for an exact output amount `outAmt_`. If available amount is\n /// less then the desired output amount, it returns the available amount. Considers the best rate available\n /// for mode with absorb and mode without absorb.\n /// @dev returned data can be fed into `getSwapCalldata` to prepare the tx that executes the swap.\n /// @param tokenIn_ input token (debt token at vault)\n /// @param tokenOut_ output token (collateral token at vault)\n /// @param outAmt_ exact output token amount that should be received as a result of the swap\n /// @return vault_ vault address at which the swap is available.\n /// @return inAmt_ required input token amount to receive `actualOutAmt_` of output token\n /// @return actualOutAmt_ actual output token amount. Equals `outAmt_`, but if less then the desired swap amount is\n /// available, then the available amount is returned instead\n /// @return withAbsorb_ flag for using mode \"withAbsorb\". Is set to true if a) liquidity without absorb would not\n /// cover the desired `outAmt_` or if b) the rate of with absorb is better than without absorb.\n function exactOutput(\n address tokenIn_,\n address tokenOut_,\n uint256 outAmt_\n ) public returns (address vault_, uint256 inAmt_, uint256 actualOutAmt_, bool withAbsorb_) {\n SwapData memory swapData_ = getSwapAvailable(tokenIn_, tokenOut_);\n vault_ = swapData_.vault;\n\n actualOutAmt_ = outAmt_; // assume outAmt_ can be covered by available amount, var is updated otherwise\n\n uint256 withAbsorbRatio_ = (swapData_.inAmtWithAbsorb * 1e27) / swapData_.outAmtWithAbsorb;\n if (outAmt_ > swapData_.outAmt && swapData_.inAmtWithAbsorb > swapData_.inAmt) {\n // with absorb has more liquidity \n withAbsorb_ = true;\n if (outAmt_ > swapData_.outAmtWithAbsorb) {\n actualOutAmt_ = swapData_.outAmtWithAbsorb; // can not cover full requested inAmt_, so set to available\n inAmt_ = swapData_.inAmtWithAbsorb;\n } else {\n // outAmt_ fully covered by with absorb liquidation, get in amount\n inAmt_ = (outAmt_ * withAbsorbRatio_) / 1e27;\n }\n } else {\n // outAmt_ is covered by available liquidation with or without absorb, check which one has better ratio\n uint256 withoutAbsorbRatio_ = (swapData_.inAmt * 1e27) / swapData_.outAmt; // in per out\n if (withAbsorbRatio_ < withoutAbsorbRatio_) {\n withAbsorb_ = true;\n inAmt_ = (outAmt_ * withAbsorbRatio_) / 1e27;\n } else {\n inAmt_ = (outAmt_ * withoutAbsorbRatio_) / 1e27;\n }\n }\n }\n}\n" }, "contracts/periphery/resolvers/vaultLiquidation/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Structs {\n struct VaultData{\n ///\n /// @param vault vault address at which the token pair is available\n address vault;\n ///\n /// @param tokenIn input token, borrow token at the vault\n address tokenIn;\n ///\n /// @param tokenOut output token, collateral token at the vault\n address tokenOut;\n }\n\n struct SwapData {\n ///\n /// @param vault vault address at which the token pair is available\n address vault;\n ///\n /// @param inAmt total input token available amount (without absorb)\n uint256 inAmt;\n ///\n /// @param outAmt total output token amount received for `inAmt` (without absorb)\n uint256 outAmt;\n ///\n /// @param inAmtWithAbsorb total input token available amount (with absorb)\n uint256 inAmtWithAbsorb;\n ///\n /// @param outAmtWithAbsorb total output token amount received for `inAmtWithAbsorb` (with absorb)\n uint256 outAmtWithAbsorb;\n }\n}\n" }, "contracts/periphery/resolvers/vaultLiquidation/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidVaultResolver } from \"../vault/iVaultResolver.sol\";\n\ncontract Variables {\n IFluidVaultResolver public immutable VAULT_RESOLVER;\n\n constructor(IFluidVaultResolver vaultResolver_) {\n VAULT_RESOLVER = vaultResolver_;\n }\n}\n" }, "contracts/periphery/resolvers/vaultPositions/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"./variables.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { IFluidVaultFactory } from \"../../../protocols/vault/interfaces/iVaultFactory.sol\";\nimport { Structs as VaultResolverStructs } from \"../vault/structs.sol\";\nimport { IFluidVaultResolver } from \"../vault/iVaultResolver.sol\";\n\ncontract FluidVaultPositionsResolver is Variables, Structs {\n /// @notice thrown if an input param address is zero\n error FluidVaultPositionsResolver__AddressZero();\n\n /// @notice constructor sets the immutable vault resolver and vault factory address\n constructor(\n IFluidVaultResolver vaultResolver_,\n IFluidVaultFactory vaultFactory_\n ) Variables(vaultResolver_, vaultFactory_) {\n if (address(vaultResolver_) == address(0) || address(vaultFactory_) == address(0)) {\n revert FluidVaultPositionsResolver__AddressZero();\n }\n }\n\n function getAllVaultNftIds(address vault_) public view returns (uint256[] memory nftIds_) {\n uint256 totalPositions_ = FACTORY.totalSupply();\n\n /// get total positions for vault: Next 32 bits => 210-241 => Total positions\n uint256 totalVaultPositions_ = (VAULT_RESOLVER.getVaultVariablesRaw(vault_) >> 210) & 0xFFFFFFFF;\n nftIds_ = new uint256[](totalVaultPositions_);\n\n // get nft Ids belonging to the vault_\n uint256 nftId_;\n uint256 j;\n for (uint256 i; i < totalPositions_; ++i) {\n nftId_ = FACTORY.tokenByIndex(i);\n if (VAULT_RESOLVER.vaultByNftId(nftId_) != vault_) {\n continue;\n }\n nftIds_[j] = nftId_;\n ++j;\n }\n }\n\n function getPositionsForNftIds(uint256[] memory nftIds_) public view returns (UserPosition[] memory positions_) {\n positions_ = new UserPosition[](nftIds_.length);\n\n VaultResolverStructs.UserPosition memory userPosition_;\n for (uint256 i; i < nftIds_.length; ++i) {\n (userPosition_, ) = VAULT_RESOLVER.positionByNftId(nftIds_[i]);\n\n positions_[i].nftId = nftIds_[i];\n positions_[i].owner = userPosition_.owner;\n positions_[i].supply = userPosition_.supply;\n positions_[i].borrow = userPosition_.borrow;\n }\n }\n\n function getAllVaultPositions(address vault_) public view returns (UserPosition[] memory positions_) {\n return getPositionsForNftIds(getAllVaultNftIds(vault_));\n }\n}\n" }, "contracts/periphery/resolvers/vaultPositions/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Structs {\n struct UserPosition {\n uint nftId;\n address owner;\n uint supply;\n uint borrow;\n }\n}\n" }, "contracts/periphery/resolvers/vaultPositions/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidVaultResolver } from \"../vault/iVaultResolver.sol\";\nimport { IFluidVaultFactory } from \"../../../protocols/vault/interfaces/iVaultFactory.sol\";\n\ncontract Variables {\n IFluidVaultResolver public immutable VAULT_RESOLVER;\n IFluidVaultFactory public immutable FACTORY;\n\n constructor(IFluidVaultResolver vaultResolver_, IFluidVaultFactory vaultFactory_) {\n VAULT_RESOLVER = vaultResolver_;\n FACTORY = vaultFactory_;\n }\n}\n" }, "contracts/periphery/resolvers/vaultTicksBranches/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { TickMath } from \"../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\nimport { IFluidVaultResolver } from \"../vault/iVaultResolver.sol\";\nimport { IFluidVaultT1 } from \"../../../protocols/vault/interfaces/iVaultT1.sol\";\n\nimport { Structs } from \"./structs.sol\";\nimport { Variables } from \"./variables.sol\";\n\n/// @notice Fluid Vault protocol ticks & branches resolver\ncontract FluidVaultTicksBranchesResolver is Variables, Structs {\n /// @notice thrown if an input param address is zero\n error FluidVaultTicksBranchesResolver__AddressZero();\n\n /// @notice constructor sets the immutable vault resolver address\n constructor(IFluidVaultResolver vaultResolver_) Variables(vaultResolver_) {\n if (address(vaultResolver_) == address(0)) {\n revert FluidVaultTicksBranchesResolver__AddressZero();\n }\n }\n\n function getTicksDebt(\n address vault_,\n int fromTick_,\n uint totalTicks_\n ) public view returns (TickDebt[] memory ticksDebt_, int toTick_) {\n int topTick_ = _tickHelper(((VAULT_RESOLVER.getVaultVariablesRaw(vault_) >> 2) & X20));\n\n fromTick_ = topTick_ < fromTick_ ? topTick_ : fromTick_;\n if (fromTick_ > type(int).min) {\n // if fromTick_ == tpye(int).min means top tick is not set, meaning no positions exist\n int startMapId_ = fromTick_ < 0 ? ((fromTick_ + 1) / 256) - 1 : fromTick_ / 256;\n // Removing all other after fromTick\n uint tickHasDebt_;\n {\n uint tickHasDebtRaw_ = VAULT_RESOLVER.getTickHasDebtRaw(vault_, startMapId_);\n\n uint bitsToRemove_ = uint(-fromTick_ + (startMapId_ * 256 + 255));\n tickHasDebt_ = (tickHasDebtRaw_ << bitsToRemove_) >> bitsToRemove_;\n }\n\n // Adding 1 here as toTick_ is inclusive in the data so if totalTicks_ = 400 then it'll only check 400\n toTick_ = fromTick_ - int(totalTicks_) + 1;\n\n uint count_ = _countTicksWithDebt(vault_, toTick_, startMapId_, tickHasDebt_);\n\n (, , uint vaultSupplyExchangePrice_, uint vaultBorrowExchangePrice_) = IFluidVaultT1(vault_)\n .updateExchangePrices(VAULT_RESOLVER.getVaultVariables2Raw(vault_));\n\n ticksDebt_ = _populateTicksDebt(\n vault_,\n toTick_,\n startMapId_,\n tickHasDebt_,\n count_,\n vaultSupplyExchangePrice_,\n vaultBorrowExchangePrice_\n );\n }\n }\n\n function getMultipleVaultsTicksDebt(\n address[] memory vaults_,\n int[] memory fromTicks_,\n uint[] memory totalTicks_\n ) public view returns (VaultsTickDebt[] memory vaultsTickDebt_) {\n uint length_ = vaults_.length;\n\n vaultsTickDebt_ = new VaultsTickDebt[](length_);\n for (uint i = 0; i < length_; i++) {\n (vaultsTickDebt_[i].tickDebt, vaultsTickDebt_[i].toTick) = getTicksDebt(\n vaults_[i],\n fromTicks_[i],\n totalTicks_[i]\n );\n }\n }\n\n function getAllVaultsTicksDebt(uint totalTicks_) public view returns (VaultsTickDebt[] memory vaultsTickDebt_) {\n address[] memory vaults_ = VAULT_RESOLVER.getAllVaultsAddresses();\n uint length_ = vaults_.length;\n\n vaultsTickDebt_ = new VaultsTickDebt[](length_);\n for (uint i = 0; i < length_; i++) {\n (vaultsTickDebt_[i].tickDebt, vaultsTickDebt_[i].toTick) = getTicksDebt(\n vaults_[i],\n type(int).max,\n totalTicks_\n );\n }\n }\n\n function getBranchesDebt(\n address vault_,\n uint fromBranchId_,\n uint toBranchId_\n ) public view returns (BranchDebt[] memory branchesDebt_) {\n uint vaultVariables_ = VAULT_RESOLVER.getVaultVariablesRaw(vault_);\n uint totalBranch_ = (vaultVariables_ >> 52) & X30;\n toBranchId_ = (toBranchId_ == 0 ? 1 : toBranchId_);\n fromBranchId_ = (totalBranch_ < fromBranchId_ ? totalBranch_ : fromBranchId_);\n\n require(fromBranchId_ >= toBranchId_, \"fromBranchId_ must be greater than or equal to toBranchId_\");\n\n branchesDebt_ = new BranchDebt[](fromBranchId_ - toBranchId_ + 1);\n\n uint index_;\n\n for (uint i = fromBranchId_; i >= toBranchId_; i--) {\n branchesDebt_[index_++] = _getBranchDebt(vault_, vaultVariables_, i);\n }\n }\n\n function getMultipleVaultsBranchesDebt(\n address[] memory vaults_,\n uint[] memory fromBranchIds_,\n uint[] memory toBranchIds_\n ) external view returns (BranchesDebt[] memory branchesDebt_) {\n uint length_ = vaults_.length;\n\n branchesDebt_ = new BranchesDebt[](length_);\n for (uint i = 0; i < length_; i++) {\n branchesDebt_[i].branchDebt = getBranchesDebt(vaults_[i], fromBranchIds_[i], toBranchIds_[i]);\n }\n }\n\n function getAllVaultsBranchesDebt() external view returns (BranchesDebt[] memory branchesDebt_) {\n address[] memory vaults_ = VAULT_RESOLVER.getAllVaultsAddresses();\n uint length_ = vaults_.length;\n\n branchesDebt_ = new BranchesDebt[](length_);\n for (uint i = 0; i < length_; i++) {\n branchesDebt_[i].branchDebt = getBranchesDebt(vaults_[i], type(uint).max, 0);\n }\n }\n\n function _populateTicksDebt(\n address vault_,\n int toTick_,\n int mapId_,\n uint tickHasDebt_,\n uint count_,\n uint vaultSupplyExchangePrice_,\n uint vaultBorrowExchangePrice_\n ) internal view returns (TickDebt[] memory ticksDebt_) {\n ticksDebt_ = new TickDebt[](count_);\n\n count_ = 0; // reuse var for loop index counter\n int nextTick_;\n uint tickExistingRawDebt_;\n uint ratio_;\n uint collateralRaw_;\n\n while (true) {\n while (tickHasDebt_ > 0) {\n {\n uint msb_ = BigMathMinified.mostSignificantBit(tickHasDebt_);\n // removing next tick from tickHasDebt\n tickHasDebt_ = (tickHasDebt_ << (257 - msb_)) >> (257 - msb_);\n nextTick_ = mapId_ * 256 + int(msb_ - 1);\n }\n if (nextTick_ < toTick_) {\n return ticksDebt_;\n }\n tickExistingRawDebt_ = (VAULT_RESOLVER.getTickDataRaw(vault_, nextTick_) >> 25) & X64;\n tickExistingRawDebt_ = (tickExistingRawDebt_ >> 8) << (tickExistingRawDebt_ & X8);\n ratio_ = TickMath.getRatioAtTick(nextTick_);\n collateralRaw_ = (tickExistingRawDebt_ * (1 << 96)) / ratio_;\n ticksDebt_[count_++] = TickDebt({\n debtRaw: tickExistingRawDebt_,\n collateralRaw: collateralRaw_,\n debtNormal: (tickExistingRawDebt_ * vaultBorrowExchangePrice_) / 1e12,\n collateralNormal: (collateralRaw_ * vaultSupplyExchangePrice_) / 1e12,\n ratio: ratio_,\n tick: nextTick_\n });\n }\n\n if (--mapId_ == -129) {\n break;\n }\n\n tickHasDebt_ = VAULT_RESOLVER.getTickHasDebtRaw(vault_, mapId_);\n }\n }\n\n function _tickHelper(uint tickRaw_) internal pure returns (int tick) {\n require(tickRaw_ < X20, \"invalid-number\");\n if (tickRaw_ > 0) {\n tick = tickRaw_ & 1 == 1 ? int((tickRaw_ >> 1) & X19) : -int((tickRaw_ >> 1) & X19);\n } else {\n tick = type(int).min;\n }\n }\n\n function _countTicksWithDebt(\n address vault_,\n int toTick_,\n int mapId_,\n uint tickHasDebt_\n ) internal view returns (uint count_) {\n uint msb_;\n int nextTick_;\n while (true) {\n while (tickHasDebt_ > 0) {\n msb_ = BigMathMinified.mostSignificantBit(tickHasDebt_);\n // removing next tick from tickHasDebt\n tickHasDebt_ = (tickHasDebt_ << (257 - msb_)) >> (257 - msb_);\n nextTick_ = mapId_ * 256 + int(msb_ - 1);\n if (nextTick_ < toTick_) {\n return count_;\n }\n count_++;\n }\n\n if (--mapId_ == -129) {\n break;\n }\n tickHasDebt_ = VAULT_RESOLVER.getTickHasDebtRaw(vault_, mapId_);\n }\n return count_;\n }\n\n function _getBranchDebt(\n address vault_,\n uint vaultVariables_,\n uint branchId_\n ) internal view returns (BranchDebt memory) {\n uint currentBranchData_ = VAULT_RESOLVER.getBranchDataRaw(vault_, branchId_);\n\n int minimaTick_ = _tickHelper((currentBranchData_ >> 2) & X20);\n uint status_ = currentBranchData_ & 3;\n\n if (status_ == 0) {\n // not liquidated status == 0\n // only current branch can be non-liquidated branch\n return _getActiveBranchDebt(vaultVariables_, currentBranchData_, branchId_, status_);\n } else if (status_ == 1) {\n // liquidated status == 1\n return _getLiquidatedBranchDebt(vault_, currentBranchData_, branchId_, status_, minimaTick_);\n } else {\n // merged status == 2\n // absorbed status == 3\n return _getClosedOrMergedBranchDebt(currentBranchData_, branchId_, status_);\n }\n }\n\n function _getActiveBranchDebt(\n uint vaultVariables_,\n uint currentBranchData_,\n uint branchId_,\n uint status_\n ) internal pure returns (BranchDebt memory branchDebt_) {\n int topTick_ = _tickHelper((vaultVariables_ >> 2) & X20);\n\n uint ratio_ = topTick_ > type(int).min ? TickMath.getRatioAtTick(topTick_) : 0;\n\n branchDebt_ = BranchDebt({\n debtRaw: 0,\n collateralRaw: 0,\n debtNormal: 0,\n collateralNormal: 0,\n branchId: branchId_,\n status: status_, // active status\n tick: topTick_, // as branch is not liquidated, just returning topTick for now, as whenever liquidation starts it'll start from topTick\n partials: 0,\n ratio: ratio_,\n debtFactor: (currentBranchData_ >> 116) & X50,\n baseBranchId: ((currentBranchData_ >> 166) & X30),\n baseBranchTick: _tickHelper((currentBranchData_ >> 196) & X20) // if == type(int).min, then current branch is master branch\n });\n }\n\n function _getClosedOrMergedBranchDebt(\n uint currentBranchData_,\n uint branchId_,\n uint status_\n ) internal pure returns (BranchDebt memory branchDebt_) {\n int baseBranchTick_ = _tickHelper((currentBranchData_ >> 196) & X20);\n uint ratio_ = baseBranchTick_ > type(int).min ? TickMath.getRatioAtTick(baseBranchTick_) : 0;\n\n branchDebt_ = BranchDebt({\n debtRaw: 0,\n collateralRaw: 0,\n debtNormal: 0,\n collateralNormal: 0,\n branchId: branchId_,\n status: status_,\n tick: baseBranchTick_, // as branch is merged/closed, so adding baseBranchTick_ as this is where it went out of existance\n partials: 0,\n ratio: ratio_,\n debtFactor: (currentBranchData_ >> 116) & X50,\n baseBranchId: ((currentBranchData_ >> 166) & X30),\n baseBranchTick: baseBranchTick_ // if == type(int).min, then current branch is master branch\n });\n }\n\n function _getLiquidatedBranchDebt(\n address vault_,\n uint currentBranchData_,\n uint branchId_,\n uint status_,\n int minimaTick_\n ) internal view returns (BranchDebt memory branchDebt_) {\n uint debtLiquidity_ = BigMathMinified.fromBigNumber((currentBranchData_ >> 52) & X64, 8, X8);\n (uint collateralRaw_, uint ratio_) = _getCollateralRaw(currentBranchData_, debtLiquidity_, minimaTick_);\n\n (, , uint256 vaultSupplyExchangePrice_, uint256 vaultBorrowExchangePrice_) = IFluidVaultT1(vault_)\n .updateExchangePrices(VAULT_RESOLVER.getVaultVariables2Raw(vault_));\n\n branchDebt_ = BranchDebt({\n debtRaw: debtLiquidity_,\n collateralRaw: collateralRaw_,\n debtNormal: (debtLiquidity_ * vaultBorrowExchangePrice_) / 1e12,\n collateralNormal: (collateralRaw_ * vaultSupplyExchangePrice_) / 1e12,\n branchId: branchId_,\n status: status_,\n tick: minimaTick_, // as branch is merged/closed, so adding baseBranchTick_ as this is where it went out of existance\n partials: 0,\n ratio: ratio_,\n debtFactor: (currentBranchData_ >> 116) & X50,\n baseBranchId: ((currentBranchData_ >> 166) & X30),\n baseBranchTick: _tickHelper((currentBranchData_ >> 196) & X20) // if == type(int).min, then current branch is master branch\n });\n }\n\n function _getCollateralRaw(\n uint currentBranchData_,\n uint debtLiquidity_,\n int minimaTick_\n ) internal pure returns (uint collateralRaw_, uint ratio_) {\n ratio_ = TickMath.getRatioAtTick(int24(minimaTick_));\n uint ratioOneLess_ = (ratio_ * 10000) / 10015;\n uint length_ = ratio_ - ratioOneLess_;\n uint partials_ = (currentBranchData_ >> 22) & X30;\n uint currentRatio_ = ratioOneLess_ + ((length_ * partials_) / X30);\n collateralRaw_ = (debtLiquidity_ * TickMath.ZERO_TICK_SCALED_RATIO) / currentRatio_;\n }\n}\n" }, "contracts/periphery/resolvers/vaultTicksBranches/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Structs {\n struct TickDebt {\n uint256 debtRaw;\n uint256 collateralRaw;\n uint256 debtNormal; // debtRaw * exchange price\n uint256 collateralNormal; // collateralRaw * exchange price\n uint256 ratio;\n int256 tick;\n }\n\n struct VaultsTickDebt {\n TickDebt[] tickDebt;\n int toTick;\n }\n\n struct BranchDebt {\n uint256 debtRaw;\n uint256 collateralRaw;\n uint256 debtNormal; // debtRaw * exchange price\n uint256 collateralNormal; // collateralRaw * exchange price\n uint256 branchId;\n uint256 status; // if 0 then not liquidated, if 1 then liquidated, if 2 then merged, if 3 then closed\n int256 tick;\n uint256 partials;\n uint256 ratio;\n uint debtFactor; // debt factor or connection factor\n uint baseBranchId;\n int baseBranchTick;\n }\n\n struct BranchesDebt {\n BranchDebt[] branchDebt;\n }\n}\n" }, "contracts/periphery/resolvers/vaultTicksBranches/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidVaultResolver } from \"../vault/iVaultResolver.sol\";\n\ncontract Variables {\n IFluidVaultResolver public immutable VAULT_RESOLVER;\n\n uint internal constant X8 = 0xff;\n uint internal constant X19 = 0x7ffff;\n uint internal constant X20 = 0xfffff;\n uint internal constant X30 = 0x3fffffff;\n uint internal constant X50 = 0x3ffffffffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n\n constructor(IFluidVaultResolver vaultResolver_) {\n VAULT_RESOLVER = vaultResolver_;\n }\n}\n" }, "contracts/periphery/wallet/factory/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Initializable } from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport { OwnableUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport { UUPSUpgradeable } from \"@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol\";\n\nimport { IERC721 } from \"@openzeppelin/contracts/interfaces/IERC721.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { Clones } from \"@openzeppelin/contracts/proxy/Clones.sol\";\n\nimport { IFluidVaultFactory } from \"../../../protocols/vault/interfaces/iVaultFactory.sol\";\n\nimport { FluidWallet } from \"../wallet/proxy.sol\";\n\ninterface InstaFlashInterface {\n function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external;\n}\n\ninterface InstaFlashReceiverInterface {\n function executeOperation(\n address[] calldata assets,\n uint256[] calldata amounts,\n uint256[] calldata premiums,\n address initiator,\n bytes calldata _data\n ) external returns (bool);\n}\n\ncontract FluidWalletFactoryVariables is Initializable, OwnableUpgradeable {\n /***********************************|\n | Constants/Immutables |\n |__________________________________*/\n IFluidVaultFactory immutable public VAULT_T1_FACTORY;\n\n address immutable public WALLET_PROXY;\n\n // ------------ storage variables from inherited contracts (Initializable, OwnableUpgradeable) come before vars here --------\n // @dev variables here start at storage slot 101, before is:\n // - Initializable with storage slot 0:\n // uint8 private _initialized;\n // bool private _initializing;\n // - OwnableUpgradeable with slots 1 to 100:\n // uint256[50] private __gap; (from ContextUpgradeable, slot 1 until slot 50)\n // address private _owner; (at slot 51)\n // uint256[49] private __gap; (slot 52 until slot 100)\n\n // ----------------------- slot 101 ---------------------------\n /// @dev fluid wallet implementation address. \n /// Can be updated by owner for newer version.\n address internal _implementation;\n\n constructor(\n address vaultT1Factory_,\n address fluidWalletFactoryProxy_\n ) {\n VAULT_T1_FACTORY = IFluidVaultFactory(vaultT1Factory_);\n WALLET_PROXY = address(new FluidWallet(address(fluidWalletFactoryProxy_)));\n }\n}\n\ncontract FluidWalletFactoryErrorsAndEvents {\n error FluidWalletFactory__InvalidOperation();\n error FluidWalletFactory__NotAllowed();\n\n event Executed(\n address indexed owner,\n uint256 indexed nft\n );\n\n event FluidImplementationUpdate(\n address indexed oldImplementation_,\n address indexed newImplementation_\n );\n}\n\ncontract FluidWalletFactory is FluidWalletFactoryVariables, FluidWalletFactoryErrorsAndEvents, UUPSUpgradeable {\n struct Action {\n address target;\n bytes data;\n uint256 value;\n uint8 operation;\n }\n\n struct StrategyParams {\n uint256 nftId;\n address owner;\n address wallet;\n Action[] actions;\n }\n\n constructor(address vaultT1Factory_, address fluidWalletFactoryProxy_) FluidWalletFactoryVariables(vaultT1Factory_, fluidWalletFactoryProxy_) {\n // ensure logic contract initializer is not abused by disabling initializing\n // see https://forum.openzeppelin.com/t/security-advisory-initialize-uups-implementation-contracts/15301\n // and https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract\n _disableInitializers();\n }\n\n /// @notice initializes the contract with `owner_` as owner\n function initialize(address owner_) public initializer {\n _transferOwnership(owner_);\n }\n\n /***********************************|\n | Action Execution |\n |__________________________________*/\n\n /// @dev ERC721 callback used Fluid Vault Factory and executes actions encoded in `data_`\n /// Caller should be Vault T1 Factory.\n /// @param operator_ operator_ caller to transfer the the given token ID\n /// @param from_ from_ previous owner of the given token ID\n /// @param tokenId_ tokenId_ id of the ERC721\n /// @param data_ data bytes containing the `abi.encoded()` actions that are executed like in `Action[]`\n function onERC721Received(\n address operator_,\n address from_,\n uint256 tokenId_,\n bytes calldata data_\n ) external returns (bytes4) {\n if (msg.sender != address(VAULT_T1_FACTORY)) revert FluidWalletFactory__NotAllowed();\n if (operator_ != from_) revert FluidWalletFactory__NotAllowed();\n if (data_.length == 0) revert FluidWalletFactory__NotAllowed();\n\n StrategyParams memory params_;\n params_.nftId = tokenId_;\n params_.owner = from_;\n params_.actions = abi.decode(data_, (Action[]));\n params_.wallet = deploy(params_.owner);\n\n VAULT_T1_FACTORY.safeTransferFrom(\n address(this),\n params_.wallet,\n params_.nftId,\n abi.encode(params_.owner, params_.actions)\n );\n\n if (VAULT_T1_FACTORY.ownerOf(tokenId_) != params_.owner) revert FluidWalletFactory__InvalidOperation();\n if (VAULT_T1_FACTORY.balanceOf(address(this)) > 0) revert FluidWalletFactory__InvalidOperation();\n\n emit Executed(params_.owner, params_.nftId);\n return this.onERC721Received.selector;\n }\n\n /***********************************|\n | Wallet Deployment functions |\n |__________________________________*/\n\n function deploy(address owner_) public returns (address wallet_) {\n bytes32 salt_ = keccak256(abi.encode(owner_));\n wallet_ = Clones.predictDeterministicAddress(WALLET_PROXY, salt_, address(this));\n if (wallet_.code.length == 0) {\n Clones.cloneDeterministic(WALLET_PROXY, salt_);\n FluidWallet(payable(wallet_)).initialize(owner_);\n }\n }\n\n function walletImplementation() public returns (address) {\n return _implementation;\n }\n\n function computeWallet(address owner_) public view returns (address wallet_) {\n return Clones.predictDeterministicAddress(WALLET_PROXY, keccak256(abi.encode(owner_)), address(this));\n }\n\n /***********************************|\n | Owner related functions |\n |__________________________________*/\n\n function spell(address[] memory targets_, bytes[] memory calldatas_) public onlyOwner {\n for (uint256 i = 0; i < targets_.length; i++) {\n Address.functionDelegateCall(targets_[i], calldatas_[i]);\n }\n }\n\n function changeImplementation(address implementation_) public onlyOwner {\n emit FluidImplementationUpdate(_implementation, implementation_);\n _implementation = implementation_;\n }\n\n /// @notice override renounce ownership as it could leave the contract in an unwanted state if called by mistake.\n function renounceOwnership() public view override onlyOwner {\n revert FluidWalletFactory__InvalidOperation();\n }\n\n receive() external payable {}\n\n function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner {}\n}" }, "contracts/periphery/wallet/factory/proxy.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { ERC1967Proxy } from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\n\n/// @title FluidWalletFactoryProxy\n/// @notice Default ERC1967Proxy for EOAStrategy\ncontract FluidWalletFactoryProxy is ERC1967Proxy {\n constructor(address logic_, bytes memory data_) payable ERC1967Proxy(logic_, data_) {}\n}\n" }, "contracts/periphery/wallet/wallet/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC721 } from \"@openzeppelin/contracts/interfaces/IERC721.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/interfaces/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { Clones } from \"@openzeppelin/contracts/proxy/Clones.sol\";\nimport { Strings } from \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IFluidVaultFactory } from \"../../../protocols/vault/interfaces/iVaultFactory.sol\";\nimport { IFluidVaultT1 } from \"../../../protocols/vault/interfaces/iVaultT1.sol\";\n\ninterface IFluidWalletFactory {\n function WALLET_PROXY() external view returns(address);\n}\n\ninterface InstaFlashReceiverInterface {\n function executeOperation(\n address[] calldata assets,\n uint256[] calldata amounts,\n uint256[] calldata premiums,\n address initiator,\n bytes calldata _data\n ) external returns (bool);\n}\n\nabstract contract FluidWalletVariables {\n /***********************************|\n | Constants/Immutables |\n |__________________________________*/\n string public constant VERSION = \"1.0.0\";\n address public constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n uint256 internal constant X32 = 0xffffffff;\n\n address public immutable VAULT_T1_FACTORY;\n address public immutable FLUID_WALLET_FACTORY;\n\n /***********************************|\n | Slot 0 |\n |__________________________________*/\n /// @dev owner address of this wallet. It is initialized while deploying the wallet for the user.\n address public owner;\n\n /***********************************|\n | Slot 1 |\n |__________________________________*/\n /// @dev transient allow hash used to signal allowing certain entry into methods such as executeOperation etc.\n bytes32 internal _transientAllowHash;\n \n function _resetTransientStorage() internal {\n assembly {\n sstore(1, 1) // Store 1 in the transient storage 1\n }\n }\n}\n\ncontract FluidWalletErrorsAndEvents {\n error FluidWallet__NotAllowed();\n error FluidWallet__ToHexDigit();\n error FluidWallet__Unauthorized();\n\n event Executed(\n address indexed owner,\n uint256 indexed tokenId\n );\n\n event ExecutedCast(address indexed owner);\n\n struct Action {\n address target;\n bytes data;\n uint256 value;\n uint8 operation;\n }\n}\n\ncontract FluidWalletImplementation is FluidWalletVariables, FluidWalletErrorsAndEvents {\n using SafeERC20 for IERC20;\n \n constructor(\n address vaultT1Factory_,\n address fluidWalletFactory_\n ) {\n VAULT_T1_FACTORY = vaultT1Factory_;\n FLUID_WALLET_FACTORY = fluidWalletFactory_;\n }\n\n /// @dev ERC721 callback used Fluid Vault Factory and executes actions encoded in `data_`\n /// Caller should be Fluid Wallet Factory.\n /// @param operator_ operator_ caller to transfer the the given token ID\n /// @param from_ from_ previous owner of the given token ID\n /// @param tokenId_ tokenId_ id of the ERC721\n /// @param data_ data bytes containing the `abi.encoded()` actions that are executed like in `Action[]` & `owner`\n function onERC721Received(\n address operator_,\n address from_,\n uint256 tokenId_,\n bytes calldata data_\n ) external returns (bytes4) {\n if (msg.sender != address(VAULT_T1_FACTORY)) revert FluidWallet__NotAllowed();\n if (operator_ != from_) revert FluidWallet__NotAllowed();\n if (operator_ != FLUID_WALLET_FACTORY) revert FluidWallet__NotAllowed();\n\n (address owner_, Action[] memory actions_) = abi.decode(data_, (address, Action[]));\n\n /// @dev validate owner by computing wallet address.\n _validateOwner(owner_);\n\n /// @dev execute actions.\n _executeActions(actions_);\n\n /// @dev reset _transientAllowHash to prevent reentrancy\n _resetTransientStorage();\n\n // Transfer tokenId back to main owner\n if (IERC721(VAULT_T1_FACTORY).ownerOf(tokenId_) == address(this)) {\n IERC721(VAULT_T1_FACTORY).transferFrom(address(this), owner_, tokenId_);\n }\n\n // flush vault specific tokens to owner address\n _flushTokens(owner_, tokenId_);\n\n emit Executed(owner_, tokenId_);\n\n return this.onERC721Received.selector;\n }\n\n function cast(\n Action[] memory actions_\n ) public {\n /// @dev validate owner by computing wallet address.\n _validateOwner(msg.sender);\n\n /// @dev execute actions.\n _executeActions(actions_);\n\n /// @dev reset _transientAllowHash to prevent reentrancy\n _resetTransientStorage();\n\n emit ExecutedCast(msg.sender);\n }\n \n\n /***********************************|\n | FLASHLOAN CALLBACK |\n |__________________________________*/\n\n /// @dev callback used by Instadapp Flashloan Aggregator, executes operations while owning\n /// the flashloaned amounts. `data_` must contain actions, one of them must pay back flashloan\n // /// @param assets_ assets_ received a flashloan for\n // /// @param amounts_ flashloaned amounts for each asset\n // /// @param premiums_ fees to pay for the flashloan\n /// @param initiator_ flashloan initiator -> must be this contract\n /// @param data_ data bytes containing the `abi.encoded()` actions that are executed like in `CastParams.actions`\n\n function executeOperation(\n address[] calldata /* assets */,\n uint256[] calldata /* amounts */,\n uint256[] calldata /* premiums */,\n address initiator_,\n bytes calldata data_\n ) external returns (bool) {\n if (\n !(_transientAllowHash ==\n bytes32(keccak256(abi.encode(data_, block.timestamp))) &&\n initiator_ == address(this))\n ) {\n revert FluidWallet__Unauthorized();\n }\n\n _executeActions(abi.decode(data_, (Action[])));\n\n return true;\n }\n\n /***********************************|\n | INTERNAL HELPERS |\n |__________________________________*/\n\n /// @notice Calculating the slot ID for Liquidity contract for single mapping\n function _calculateStorageSlotUintMapping(uint256 slot_, uint key_) internal pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n function _flushTokens(address owner_, uint256 tokenId_) internal {\n uint256 tokenConfig_ = IFluidVaultFactory(VAULT_T1_FACTORY).readFromStorage(_calculateStorageSlotUintMapping(3, tokenId_));\n address vaultAddress_ = IFluidVaultFactory(VAULT_T1_FACTORY).getVaultAddress((tokenConfig_ >> 192) & X32);\n\n IFluidVaultT1.ConstantViews memory constants_ = IFluidVaultT1(vaultAddress_).constantsView();\n\n if (constants_.supplyToken == ETH_ADDRESS) {\n uint256 balance_ = address(this).balance;\n \n if (balance_ > 0) Address.sendValue(payable(owner_), balance_);\n } else {\n uint256 balance_ = IERC20(constants_.supplyToken).balanceOf(address(this));\n\n if (balance_ > 0) IERC20(constants_.supplyToken).safeTransfer(owner_, balance_);\n }\n \n if (constants_.borrowToken == ETH_ADDRESS ) {\n uint256 balance_ = address(this).balance;\n \n if (balance_ > 0) Address.sendValue(payable(owner_), balance_);\n } else {\n uint256 balance_ = IERC20(constants_.borrowToken).balanceOf(address(this));\n\n if (balance_ > 0) IERC20(constants_.borrowToken).safeTransfer(owner_, balance_);\n }\n }\n\n /// @dev validate `owner` by recomputing fluid address.\n function _validateOwner(address owner_) internal view {\n address wallet_ = Clones.predictDeterministicAddress(\n IFluidWalletFactory(FLUID_WALLET_FACTORY).WALLET_PROXY(),\n keccak256(abi.encode(owner_)),\n FLUID_WALLET_FACTORY\n );\n if (wallet_ != address(this)) revert FluidWallet__NotAllowed();\n }\n\n /// @dev executes `actions_` with respective target, calldata, operation etc.\n function _executeActions(Action[] memory actions_) internal {\n uint256 actionsLength_ = actions_.length;\n for (uint256 i; i < actionsLength_; ) {\n Action memory action_ = actions_[i];\n\n // execute action\n bool success_;\n bytes memory result_;\n if (action_.operation == 0) {\n // call (operation = 0)\n\n // low-level call will return success true also if action target is not even a contract.\n // we do not explicitly check for this, default interaction is via UI which can check and handle this.\n // Also applies to delegatecall etc.\n (success_, result_) = action_.target.call{ value: action_.value }(action_.data);\n\n // handle action failure right after external call to better detect out of gas errors\n if (!success_) {\n _handleActionFailure(i, result_);\n }\n } else if (action_.operation == 1) {\n // delegatecall (operation = 1)\n\n (success_, result_) = action_.target.delegatecall(action_.data);\n\n // reset _transientAllowHash to make sure it can not be set up in any way for reentrancy\n _resetTransientStorage();\n\n // handle action failure right after external call to better detect out of gas errors\n if (!success_) {\n _handleActionFailure(i, result_);\n }\n } else if (action_.operation == 2) {\n // flashloan (operation = 2)\n // flashloan is always executed via .call, flashloan aggregator uses `msg.sender`, so .delegatecall\n // wouldn't send funds to this contract but rather to the original sender.\n\n bytes memory data_ = action_.data;\n assembly {\n data_ := add(data_, 4) // Skip function selector (4 bytes)\n }\n // get actions data from calldata action_.data. Only supports InstaFlashAggregatorInterface\n (, , , data_, ) = abi.decode(data_, (address[], uint256[], uint256, bytes, bytes));\n\n // set allowHash to signal allowed entry into executeOperation()\n _transientAllowHash = bytes32(\n keccak256(abi.encode(data_, block.timestamp))\n );\n\n // handle action failure right after external call to better detect out of gas errors\n (success_, result_) = action_.target.call{ value: action_.value }(action_.data);\n\n if (!success_) {\n _handleActionFailure(i, result_);\n }\n\n // reset _transientAllowHash to prevent reentrancy during actions execution\n _resetTransientStorage();\n } else {\n // either operation does not exist or the id was not set according to what the action wants to execute\n revert(string.concat(Strings.toString(i), \"_FLUID__INVALID_ID_OR_OPERATION\"));\n }\n\n unchecked {\n ++i;\n }\n }\n }\n\n /// @dev handles failure of an action execution depending on error cause,\n /// decoding and reverting with `result_` as reason string.\n function _handleActionFailure(uint256 i_, bytes memory result_) internal pure {\n revert(string.concat(Strings.toString(i_), _getRevertReasonFromReturnedData(result_)));\n }\n\n uint256 internal constant REVERT_REASON_MAX_LENGTH = 250;\n\n /// @dev Get the revert reason from the returnedData (supports Panic, Error & Custom Errors).\n /// Based on https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/libs/CallUtils.sol\n /// This is needed in order to provide some human-readable revert message from a call.\n /// @param returnedData_ revert data of the call\n /// @return reason_ revert reason\n function _getRevertReasonFromReturnedData(\n bytes memory returnedData_\n ) internal pure returns (string memory reason_) {\n if (returnedData_.length < 4) {\n // case 1: catch all\n return \"_REASON_NOT_DEFINED\";\n }\n\n bytes4 errorSelector_;\n assembly {\n errorSelector_ := mload(add(returnedData_, 0x20))\n }\n if (errorSelector_ == bytes4(0x4e487b71)) {\n // case 2: Panic(uint256), selector 0x4e487b71 (Defined since 0.8.0)\n // ref: https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require)\n\n // convert last byte to hex digits -> string to decode the panic code\n bytes memory result_ = new bytes(2);\n result_[0] = _toHexDigit(uint8(returnedData_[returnedData_.length - 1]) / 16);\n result_[1] = _toHexDigit(uint8(returnedData_[returnedData_.length - 1]) % 16);\n reason_ = string.concat(\"_TARGET_PANICKED: 0x\", string(result_));\n } else if (errorSelector_ == bytes4(0x08c379a0)) {\n // case 3: Error(string), selector 0x08c379a0 (Defined at least since 0.7.0)\n // based on https://ethereum.stackexchange.com/a/83577\n assembly {\n returnedData_ := add(returnedData_, 0x04)\n }\n reason_ = string.concat(\"_\", abi.decode(returnedData_, (string)));\n } else {\n // case 4: Custom errors (Defined since 0.8.0)\n\n // convert bytes4 selector to string, params are ignored...\n // based on https://ethereum.stackexchange.com/a/111876\n bytes memory result_ = new bytes(8);\n for (uint256 i; i < 4; ) {\n // use unchecked as i is < 4 and division. also errorSelector can not underflow\n unchecked {\n result_[2 * i] = _toHexDigit(uint8(errorSelector_[i]) / 16);\n result_[2 * i + 1] = _toHexDigit(uint8(errorSelector_[i]) % 16);\n ++i;\n }\n }\n reason_ = string.concat(\"_CUSTOM_ERROR: 0x\", string(result_));\n }\n\n {\n // truncate reason_ string to REVERT_REASON_MAX_LENGTH for reserveGas used to ensure Cast event is emitted\n if (bytes(reason_).length > REVERT_REASON_MAX_LENGTH) {\n bytes memory reasonBytes_ = bytes(reason_);\n uint256 maxLength_ = REVERT_REASON_MAX_LENGTH + 1; // cheaper than <= in each loop\n bytes memory truncatedRevertReason_ = new bytes(maxLength_);\n for (uint256 i; i < maxLength_; ) {\n truncatedRevertReason_[i] = reasonBytes_[i];\n\n unchecked {\n ++i;\n }\n }\n reason_ = string(truncatedRevertReason_);\n }\n }\n }\n\n /// @dev used to convert bytes4 selector to string\n function _toHexDigit(uint8 d) internal pure returns (bytes1) {\n // use unchecked as the operations with d can not over / underflow\n unchecked {\n if (d < 10) {\n return bytes1(uint8(bytes1(\"0\")) + d);\n }\n if (d < 16) {\n return bytes1(uint8(bytes1(\"a\")) + d - 10);\n }\n }\n revert FluidWallet__ToHexDigit();\n }\n}" }, "contracts/periphery/wallet/wallet/proxy.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidWalletFactory {\n function walletImplementation() external returns(address);\n}\n\n/// @title FluidWallet\n/// @notice Proxy for Fluid wallet as deployed by the FluidWalletFactory.\n/// Basic Proxy with fallback to delegate and address for implementation contract at storage 0x0\n//\ncontract FluidWallet {\n /// @notice Fluid Wallet Factory address.\n IFluidWalletFactory public immutable FACTORY;\n\n /// @notice Owner of the wallet.\n address public owner;\n\n function initialize(address owner_) public {\n if (owner == address(0)) {\n owner = owner_;\n } else {\n revert();\n }\n }\n\n constructor (address factory_) {\n FACTORY = IFluidWalletFactory(factory_);\n }\n\n receive() external payable {}\n\n fallback() external payable {\n address impl_ = FACTORY.walletImplementation();\n assembly {\n // @dev code below is taken from OpenZeppelin Proxy.sol _delegate function\n\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), impl_, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n}" }, "contracts/protocols/flashloan/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Error {\n error FluidFlashLenderError(uint256 errorId_);\n}\n" }, "contracts/protocols/flashloan/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | FlashLender | \n |__________________________________*/\n\n /// @notice Thrown when a function encounters or is provided with an address set to the zero address.\n uint256 internal constant FlashLender__ZeroAddress = 80001;\n\n /// @notice Thrown when the requested flash loan amount exceeds the allowed maximum.\n uint256 internal constant FlashLender__MaxFlashLoanAmountExceeded = 80002;\n\n /// @notice Thrown when a function returns an unexpected value.\n uint256 internal constant FlashLender__InvalidReturnValue = 80003;\n\n /// @notice Thrown when an unauthorized action is attempted.\n uint256 internal constant FlashLender__Unauthorized = 80004;\n\n /// @notice Thrown when an operation involves a token that isn't supported.\n uint256 internal constant FlashLender__TokenNotSupported = 80005;\n\n /// @notice Thrown when a token is already being borrowed by flashloan protocol.\n uint256 internal constant FlashLender__DuplicateBorrow = 80006;\n}\n" }, "contracts/protocols/flashloan/helpers.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\n\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { Error } from \"./error.sol\";\nimport { Variables } from \"./variables.sol\";\n\n/// @dev Flashloan protocol helper methods\nabstract contract Helpers is Variables, Error {\n constructor(IFluidLiquidity liquidity_) Variables(liquidity_) {}\n\n /// @notice Reads user borrow data of currency to be lent.\n /// @param token_ The loan currency.\n /// @return user borrow data for `token`.\n function _readUserBorrowData(address token_) internal view returns (uint256) {\n bytes32 slot_ = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT,\n address(this),\n token_\n );\n return LIQUIDITY.readFromStorage(slot_);\n }\n\n /// @notice Checks if the given address is valid (not equal to address(0)).\n /// @param value_ The address to check..\n function _checkValidAddress(address value_) internal pure {\n if (value_ == address(0)) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__ZeroAddress);\n }\n }\n\n /// @notice Ensures that the provided address is valid (not equal to address(0)).\n /// @param value_ The address to check.\n modifier validAddress(address value_) {\n _checkValidAddress(value_);\n _;\n }\n}\n" }, "contracts/protocols/flashloan/interfaces/IERC3156FlashBorrower.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IERC3156FlashBorrower {\n function onFlashLoan(\n address initiator,\n address token,\n uint256 amount,\n uint256 fee,\n bytes calldata data\n ) external returns (bytes32);\n}\n" }, "contracts/protocols/flashloan/interfaces/IERC3156FlashLender.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport \"./IERC3156FlashBorrower.sol\";\n\ninterface IERC3156FlashLender {\n /// @dev The amount of currency available to be lent.\n /// @param token The loan currency.\n /// @return The amount of `token` that can be borrowed.\n function maxFlashLoan(address token) external view returns (uint256);\n\n /// @dev The fee to be charged for a given loan.\n /// @param token The loan currency.\n /// @param amount The amount of tokens lent.\n /// @return The amount of `token` to be charged for the loan, on top of the returned principal.\n function flashFee(address token, uint256 amount) external view returns (uint256);\n\n /// @dev Initiate a flash loan.\n /// @param receiver The receiver of the tokens in the loan, and the receiver of the callback.\n /// @param token The loan currency.\n /// @param amount The amount of tokens lent.\n /// @param data Arbitrary data structure, intended to contain user-defined parameters.\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n ) external returns (bool);\n}\n" }, "contracts/protocols/flashloan/interfaces/iFlashBorrower.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport \"./IERC3156FlashBorrower.sol\";\nimport { Structs } from \"../structs.sol\";\n\ninterface IFlashBorrower is IERC3156FlashBorrower {\n function onFlashLoanMultiple(\n address initiator,\n Structs.AddressUint256[] calldata tokensWithValues,\n uint256 fee,\n bytes calldata data\n ) external returns (bytes32);\n}\n" }, "contracts/protocols/flashloan/interfaces/iFlashLender.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IFlashBorrower } from \"./iFlashBorrower.sol\";\nimport { IERC3156FlashLender } from \"./IERC3156FlashLender.sol\";\nimport { IERC3156FlashBorrower } from \"./IERC3156FlashBorrower.sol\";\nimport { Structs } from \"../structs.sol\";\n\ninterface IFluidFlashLender is IERC3156FlashLender {\n /// @notice Initiate a flash loan with multiple tokens.\n /// @param receiver_ The receiver of the tokens in the loan, and the receiver of the callback.\n /// @param tokensWithAmounts_ An array of token addresses and loan amounts.\n /// @param data_ Arbitrary data structure, intended to contain user-defined parameters.\n /// @return 'true' if the flash loan are successful\n function flashLoanMultiple(\n IFlashBorrower receiver_,\n Structs.AddressUint256[] calldata tokensWithAmounts_,\n bytes calldata data_\n ) external returns (bool);\n}\n" }, "contracts/protocols/flashloan/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { Helpers } from \"./helpers.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { IFluidFlashLender } from \"./interfaces/iFlashLender.sol\";\nimport { IERC3156FlashBorrower } from \"./interfaces/IERC3156FlashBorrower.sol\";\nimport { IFlashBorrower } from \"./interfaces/iFlashBorrower.sol\";\n\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\nimport { IProtocol } from \"../../liquidity/userModule/main.sol\";\n\nimport { LiquidityCalcs } from \"../../libraries/liquidityCalcs.sol\";\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\n\n/// @notice contract that provides Flashloans via Fluid. ERC-3156 compatible.\n/// In addition also implements a `flashLoanMultiple()` method which can be used to flash loan multiple tokens at once.\n/// @dev Fee is a constant 0. This contract is not upgradeable.\n//\n// @dev Fee is not really implemented, just the parts that are needed to ensure compatibility with ERC-3156.\n// @dev user borrow amount for this contract at Liquidity will slowly grow but that is not an issue as the amount\n// being repaid is still the exact original amount always.\ncontract FluidFlashLender is Structs, IFluidFlashLender, IProtocol, Helpers {\n using SafeERC20 for IERC20;\n\n /// @notice emitted whenever funds for a certain `token` are rescued to Liquidity\n event LogRescueFunds(address indexed token);\n\n /// @notice emitted when a flashloan has been taken and repaid\n event LogFlashLoan(address indexed token, uint256 indexed amount, address indexed receiver);\n\n constructor(IFluidLiquidity liquidity_) Helpers(liquidity_) {}\n\n /// @notice The amount of currency available to be lent.\n /// @param token_ The loan currency.\n /// @return The amount of `token` that can be borrowed.\n function maxFlashLoan(address token_) public view returns (uint256) {\n // maximum flashloan is either:\n // a) the borrow limit of Flashloan protocol at Liquidity or\n // b) the total available amount of token at Liquidity (if lower than borrow limit)\n\n // get current exchange rates\n (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n token_\n )\n )\n );\n\n // a) get the borrow limit\n uint256 userBorrowData_ = _readUserBorrowData(token_);\n uint256 userBorrow_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & LiquidityCalcs.X64;\n // get borrow limit\n uint256 borrowLimit_ = LiquidityCalcs.calcBorrowLimitBeforeOperate(userBorrowData_, userBorrow_);\n // revert if Flashloan protocol has already borrowed more than 0.0001% of borrow limit for the `token_`. This\n // catches any duplicate token borrows. We can not just check for user borrow == 0 because of rounding.\n if (userBorrow_ > (borrowLimit_ / 100_000)) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__DuplicateBorrow);\n }\n // subtract any already borrowed amount\n borrowLimit_ -= userBorrow_;\n\n if (userBorrowData_ & 1 == 1) {\n // convert raw borrow limit amount to normal for withInterest mode\n borrowLimit_ = (borrowLimit_ * borrowExchangePrice_) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n }\n\n // b) get the total available amount at Liquidity (total supply - total borrow)\n uint256 totalAmounts_ = LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n token_\n )\n );\n // totalSupply = supplyRawInterest normalized from raw + supplyInterestFree\n uint256 totalSupply_ = LiquidityCalcs.getTotalSupply(totalAmounts_, supplyExchangePrice_);\n // get total borrow for token interest free + with interest converted from raw\n uint256 totalBorrow_ = LiquidityCalcs.getTotalBorrow(totalAmounts_, borrowExchangePrice_);\n\n uint256 availableAmount_;\n unchecked {\n // todo: this amount would also have to include max utilization limit.\n // potential other updates might be needed in flashloan protocol before release as Liquidity layer\n // had some changes etc.\n availableAmount_ = totalBorrow_ > totalSupply_ ? 0 : totalSupply_ - totalBorrow_;\n }\n\n return borrowLimit_ < availableAmount_ ? borrowLimit_ : availableAmount_;\n }\n\n /// @notice The fee to be charged for a given loan.\n /// @param token_ loan currency.\n /// @param amount_ The amount of tokens lent.\n /// @return The amount of `token` to be charged for the loan, on top of the returned principal.\n function flashFee(address token_, uint256 amount_) external view returns (uint256) {\n if (_readUserBorrowData(token_) == 0) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__TokenNotSupported);\n }\n return _FLASH_FEE;\n }\n\n /// @notice Initiate a flash loan.\n /// @dev receiver_ must approve `amount_` of `token_` for payback. For native token, it must be send as msg.value.\n /// @param receiver_ The receiver of the tokens in the loan, and the receiver of the callback.\n /// @param token_ The loan currency.\n /// @param amount_ The amount of tokens lent.\n /// @param data_ Arbitrary data structure, intended to contain user-defined parameters.\n function flashLoan(\n IERC3156FlashBorrower receiver_,\n address token_,\n uint256 amount_,\n bytes calldata data_\n ) external validAddress(address(receiver_)) validAddress(token_) returns (bool) {\n if (amount_ > maxFlashLoan(token_)) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__MaxFlashLoanAmountExceeded);\n }\n\n LIQUIDITY.operate(token_, 0, SafeCast.toInt256(amount_), address(0), address(receiver_), new bytes(0));\n\n if (\n receiver_.onFlashLoan(msg.sender, token_, amount_, _FLASH_FEE, data_) != _RETURN_VALUE_FOR_FLASHLOAN_SINGLE\n ) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__InvalidReturnValue);\n }\n\n LIQUIDITY.operate{ value: token_ == _NATIVE_TOKEN_ADDRESS ? amount_ : 0 }(\n token_,\n 0,\n -int256(amount_),\n address(0),\n address(0),\n abi.encode(address(receiver_))\n );\n\n emit LogFlashLoan(token_, amount_, address(receiver_));\n\n return true;\n }\n\n /// @notice Initiate a flash loan with multiple tokens.\n /// @dev receiver_ must approve `amount_` of each token for payback. For native token, it must be send as msg.value.\n /// @param receiver_ The receiver of the tokens in the loan, and the receiver of the callback.\n /// @param tokensWithAmounts_ An array of token addresses and loan amounts.\n /// @param data_ Arbitrary data structure, intended to contain user-defined parameters.\n /// @return 'true' if the flash loans are successful\n function flashLoanMultiple(\n IFlashBorrower receiver_,\n AddressUint256[] calldata tokensWithAmounts_,\n bytes calldata data_\n ) external validAddress(address(receiver_)) returns (bool) {\n uint256 length_ = tokensWithAmounts_.length;\n\n // execute all borrows at Liquidity\n for (uint256 i; i < length_; ) {\n _checkValidAddress(tokensWithAmounts_[i].addr);\n if (tokensWithAmounts_[i].value > maxFlashLoan(tokensWithAmounts_[i].addr)) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__MaxFlashLoanAmountExceeded);\n }\n\n LIQUIDITY.operate(\n tokensWithAmounts_[i].addr,\n 0,\n SafeCast.toInt256(tokensWithAmounts_[i].value),\n address(0),\n address(receiver_),\n new bytes(0)\n );\n\n unchecked {\n i++;\n }\n }\n\n if (\n //onFlashLoanMultiple or onFlashLoan callback and then we could put tokens with amounts if data callback\n receiver_.onFlashLoanMultiple(msg.sender, tokensWithAmounts_, _FLASH_FEE, data_) !=\n _RETURN_VALUE_FOR_FLASHLOAN_MULTIPLE\n ) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__InvalidReturnValue);\n }\n\n // execute all paybacks at Liquidity\n for (uint256 i; i < length_; ) {\n {\n address receiverAddress_ = address(receiver_); // required to avoid stack too deep error\n\n LIQUIDITY.operate{\n value: tokensWithAmounts_[i].addr == _NATIVE_TOKEN_ADDRESS ? tokensWithAmounts_[i].value : 0\n }(\n tokensWithAmounts_[i].addr,\n 0,\n -int256(tokensWithAmounts_[i].value),\n address(0),\n address(0),\n abi.encode(receiverAddress_)\n );\n\n emit LogFlashLoan(tokensWithAmounts_[i].addr, tokensWithAmounts_[i].value, receiverAddress_);\n }\n\n unchecked {\n i++;\n }\n }\n\n return true;\n }\n\n /// @notice flashLender liquidity callback\n /// @param token_ The token being transferred\n /// @param amount_ The amount being transferred\n function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external {\n if (msg.sender != address(LIQUIDITY)) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__Unauthorized);\n }\n\n address receiver_ = abi.decode(data_, (address));\n IERC20(token_).safeTransferFrom(receiver_, address(LIQUIDITY), amount_);\n }\n\n /// @dev @Fallback function to receive Ether\n receive() external payable {}\n\n /// @notice sends any potentially stuck funds to Liquidity contract. Only callable by Liquidity auths.\n //\n // @dev this contract never holds any funds:\n // -> borrowed funds are directly sent from Liquidity to the receiver.\n // -> at payback, only approval is given by receiver to this contract but funds are directly transferred from\n // receiver to Liquidity. For native token, msg.value is sent forward to Liquidity operate() call.\n // Thus we can simply create a method to rescue any potentially stuck funds by sending them to Liquidity,\n // where that excess amount is recognized as revenue and could be withdrawn by Governance.\n function rescueFunds(address token_) external {\n if (\n // msg.sender must be an auth or Governance at Liquidity\n (LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_AUTHS_MAPPING_SLOT,\n msg.sender\n )\n ) &\n 1 !=\n 1) && address(uint160(LIQUIDITY.readFromStorage(_GOVERNANCE_SLOT))) != msg.sender\n ) {\n revert FluidFlashLenderError(ErrorTypes.FlashLender__Unauthorized);\n }\n\n if (token_ == _NATIVE_TOKEN_ADDRESS) {\n Address.sendValue(payable(address(LIQUIDITY)), address(this).balance);\n } else {\n IERC20(token_).safeTransfer(address(LIQUIDITY), IERC20(token_).balanceOf(address(this)));\n }\n\n emit LogRescueFunds(token_);\n }\n\n /// @notice repays `repayAmount_` for `token_` at Liquidity.\n /// Can be used to balance out any leftover rounding differences at Liquidity if ever needed.\n function repayAtLiquidity(address token_, uint256 repayAmount_) external payable {\n LIQUIDITY.operate{ value: msg.value }(\n token_,\n 0,\n -int256(repayAmount_),\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n }\n}\n" }, "contracts/protocols/flashloan/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Structs {\n struct AddressUint256 {\n address addr;\n uint256 value;\n }\n}\n" }, "contracts/protocols/flashloan/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\n\nabstract contract Variables {\n /// @dev address that is mapped to the chain native token\n address internal constant _NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n bytes32 internal constant _RETURN_VALUE_FOR_FLASHLOAN_SINGLE = keccak256(\"ERC3156FlashBorrower.onFlashLoan\");\n bytes32 internal constant _RETURN_VALUE_FOR_FLASHLOAN_MULTIPLE = keccak256(\"FlashBorrower.onFlashLoanMultiple\");\n\n /// @dev Storage slot with the admin of the contract at Liquidity. Logic from \"proxy.sol\".\n /// This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1.\n bytes32 internal constant _GOVERNANCE_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /// @dev fee for flashloan set globally for all tokens\n //\n // @dev can't just change this to a value != 0 as that case is not fully implemented. Ensure necessary changes are\n // made if _FLASH_FEE must be set to value != 0.\n uint256 internal constant _FLASH_FEE = 0;\n\n /// @notice address of the Fluid Liquidity contract\n IFluidLiquidity public immutable LIQUIDITY;\n\n constructor(IFluidLiquidity liquidity_) {\n LIQUIDITY = liquidity_;\n }\n}\n" }, "contracts/protocols/lending/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Error {\n error FluidLendingError(uint256 errorId_);\n}\n" }, "contracts/protocols/lending/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | fToken | \n |__________________________________*/\n\n /// @notice thrown when a deposit amount is too small to increase BigMath stored balance in Liquidity.\n /// precision of BigMath is 1e12, so if token holds 120_000_000_000 USDC, min amount to make a difference would be 0.1 USDC.\n /// i.e. user would send a very small deposit which mints no shares -> revert\n uint256 internal constant fToken__DepositInsignificant = 20001;\n\n /// @notice thrown when minimum output amount is not reached, e.g. for minimum shares minted (deposit) or\n /// minimum assets received (redeem)\n uint256 internal constant fToken__MinAmountOut = 20002;\n\n /// @notice thrown when maximum amount is surpassed, e.g. for maximum shares burned (withdraw) or\n /// maximum assets input (mint)\n uint256 internal constant fToken__MaxAmount = 20003;\n\n /// @notice thrown when invalid params are sent to a method, e.g. zero address\n uint256 internal constant fToken__InvalidParams = 20004;\n\n /// @notice thrown when an unauthorized caller is trying to execute an auth-protected method\n uint256 internal constant fToken__Unauthorized = 20005;\n\n /// @notice thrown when a with permit / signature method is called from msg.sender that is the owner.\n /// Should call the method without permit instead if msg.sender is the owner.\n uint256 internal constant fToken__PermitFromOwnerCall = 20006;\n\n /// @notice thrown when a reentrancy is detected.\n uint256 internal constant fToken__Reentrancy = 20007;\n\n /// @notice thrown when _tokenExchangePrice overflows type(uint64).max\n uint256 internal constant fToken__ExchangePriceOverflow = 20008;\n\n /// @notice thrown when msg.sender is not rebalancer\n uint256 internal constant fToken__NotRebalancer = 20009;\n\n /// @notice thrown when rebalance is called with msg.value > 0 for non NativeUnderlying fToken\n uint256 internal constant fToken__NotNativeUnderlying = 20010;\n\n /// @notice thrown when the received new liquidity exchange price is of unexpected value (< than the old one)\n uint256 internal constant fToken__LiquidityExchangePriceUnexpected = 20011;\n\n /***********************************|\n | fToken Native Underlying | \n |__________________________________*/\n\n /// @notice thrown when native deposit is called but sent along `msg.value` does not cover the deposit amount\n uint256 internal constant fTokenNativeUnderlying__TransferInsufficient = 21001;\n\n /// @notice thrown when a liquidity callback is called for a native token operation\n uint256 internal constant fTokenNativeUnderlying__UnexpectedLiquidityCallback = 21002;\n\n /***********************************|\n | Lending Factory | \n |__________________________________*/\n\n /// @notice thrown when a method is called with invalid params\n uint256 internal constant LendingFactory__InvalidParams = 22001;\n\n /// @notice thrown when the provided input param address is zero\n uint256 internal constant LendingFactory__ZeroAddress = 22002;\n\n /// @notice thrown when the token already exists\n uint256 internal constant LendingFactory__TokenExists = 22003;\n\n /// @notice thrown when the fToken has not yet been configured at Liquidity\n uint256 internal constant LendingFactory__LiquidityNotConfigured = 22004;\n\n /// @notice thrown when an unauthorized caller is trying to execute an auth-protected method\n uint256 internal constant LendingFactory__Unauthorized = 22005;\n\n /***********************************|\n | Lending Rewards Rate Model | \n |__________________________________*/\n\n /// @notice thrown when invalid params are given as input\n uint256 internal constant LendingRewardsRateModel__InvalidParams = 23001;\n\n /// @notice thrown when calculated rewards rate is exceeding the maximum rate\n uint256 internal constant LendingRewardsRateModel__MaxRate = 23002;\n\n /// @notice thrown when start is called by any other address other than initiator\n uint256 internal constant LendingRewardsRateModel__NotTheInitiator = 23003;\n\n /// @notice thrown when start is called after the rewards are already started\n uint256 internal constant LendingRewardsRateModel__AlreadyStarted = 23004;\n\n /// @notice thrown when the provided input param address is zero\n uint256 internal constant LendingRewardsRateModel__ZeroAddress = 23005;\n}\n" }, "contracts/protocols/lending/fToken/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLendingRewardsRateModel } from \"../interfaces/iLendingRewardsRateModel.sol\";\n\nabstract contract Events {\n /// @notice emitted whenever admin updates rewards rate model\n event LogUpdateRewards(IFluidLendingRewardsRateModel indexed rewardsRateModel);\n\n /// @notice emitted whenever rebalance is executed to fund difference between Liquidity deposit and totalAssets()\n /// as rewards through the rebalancer.\n event LogRebalance(uint256 assets);\n\n /// @notice emitted whenever exchange rates are updated\n event LogUpdateRates(uint256 tokenExchangePrice, uint256 liquidityExchangePrice);\n\n /// @notice emitted whenever funds for a certain `token` are rescued to Liquidity\n event LogRescueFunds(address indexed token);\n\n /// @notice emitted whenever rebalancer address is updated\n event LogUpdateRebalancer(address indexed rebalancer);\n}\n" }, "contracts/protocols/lending/fToken/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\nimport { FixedPointMathLib } from \"solmate/src/utils/FixedPointMathLib.sol\";\nimport { IERC20Permit } from \"@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IAllowanceTransfer } from \"../interfaces/permit2/iAllowanceTransfer.sol\";\nimport { IFluidLendingRewardsRateModel } from \"../interfaces/iLendingRewardsRateModel.sol\";\nimport { IFluidLendingFactory } from \"../interfaces/iLendingFactory.sol\";\nimport { IFToken, IFTokenAdmin } from \"../interfaces/iFToken.sol\";\nimport { LiquidityCalcs } from \"../../../libraries/liquidityCalcs.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { SafeTransfer } from \"../../../libraries/safeTransfer.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { Variables } from \"./variables.sol\";\nimport { Events } from \"./events.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\n/// @dev ReentrancyGuard based on OpenZeppelin implementation.\n/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/security/ReentrancyGuard.sol\nabstract contract ReentrancyGuard is Error, Variables {\n uint8 internal constant REENTRANCY_NOT_ENTERED = 1;\n uint8 internal constant REENTRANCY_ENTERED = 2;\n\n constructor() {\n _status = REENTRANCY_NOT_ENTERED;\n }\n\n /// @dev checks that no reentrancy occurs, reverts if so. Calling the method in the modifier reduces\n /// bytecode size as modifiers are inlined into bytecode\n function _checkReentrancy() internal {\n // On the first call to nonReentrant, _status will be NOT_ENTERED\n if (_status != REENTRANCY_NOT_ENTERED) {\n revert FluidLendingError(ErrorTypes.fToken__Reentrancy);\n }\n\n // Any calls to nonReentrant after this point will fail\n _status = REENTRANCY_ENTERED;\n }\n\n /// @dev Prevents a contract from calling itself, directly or indirectly.\n /// See OpenZeppelin implementation for more info\n modifier nonReentrant() {\n _checkReentrancy();\n\n _;\n\n // storing original value triggers a refund (see https://eips.ethereum.org/EIPS/eip-2200)\n _status = REENTRANCY_NOT_ENTERED;\n }\n}\n\n/// @dev internal methods for fToken contracts\nabstract contract fTokenCore is Error, IERC4626, IFToken, Variables, Events, ReentrancyGuard {\n using FixedPointMathLib for uint256;\n\n /// @dev Gets current (updated) Liquidity supply exchange price for the underyling asset\n function _getLiquidityExchangePrice() internal view returns (uint256 supplyExchangePrice_) {\n (supplyExchangePrice_, ) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_EXCHANGE_PRICES_SLOT)\n );\n }\n\n /// @dev Gets current Liquidity supply balance of `address(this)` for the underyling asset\n function _getLiquidityBalance() internal view returns (uint256 balance_) {\n // extract user supply amount\n uint256 userSupplyRaw_ = BigMathMinified.fromBigNumber(\n (LIQUIDITY.readFromStorage(LIQUIDITY_USER_SUPPLY_SLOT) >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) &\n LiquidityCalcs.X64,\n LiquidityCalcs.DEFAULT_EXPONENT_SIZE,\n LiquidityCalcs.DEFAULT_EXPONENT_MASK\n );\n\n unchecked {\n // can not overflow as userSupplyRaw_ can be maximally type(int128).max, liquidity exchange price type(uint64).max\n return (userSupplyRaw_ * _getLiquidityExchangePrice()) / EXCHANGE_PRICES_PRECISION;\n }\n }\n\n /// @dev Gets current Liquidity underlying token balance\n function _getLiquidityUnderlyingBalance() internal view virtual returns (uint256) {\n return ASSET.balanceOf(address(LIQUIDITY));\n }\n\n /// @dev Gets current withdrawable amount at Liquidity `withdrawalLimit_` (withdrawal limit or balance).\n function _getLiquidityWithdrawable() internal view returns (uint256 withdrawalLimit_) {\n uint256 userSupplyData_ = LIQUIDITY.readFromStorage(LIQUIDITY_USER_SUPPLY_SLOT);\n uint256 userSupply_ = BigMathMinified.fromBigNumber(\n (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & LiquidityCalcs.X64,\n LiquidityCalcs.DEFAULT_EXPONENT_SIZE,\n LiquidityCalcs.DEFAULT_EXPONENT_MASK\n );\n withdrawalLimit_ = LiquidityCalcs.calcWithdrawalLimitBeforeOperate(userSupplyData_, userSupply_);\n\n // convert raw amounts to normal amounts\n unchecked {\n // can not overflow as userSupply_ can be maximally type(int128).max\n // and withdrawalLimit is smaller than userSupply_\n uint256 liquidityExchangePrice_ = _getLiquidityExchangePrice();\n withdrawalLimit_ = (withdrawalLimit_ * liquidityExchangePrice_) / EXCHANGE_PRICES_PRECISION;\n userSupply_ = (userSupply_ * liquidityExchangePrice_) / EXCHANGE_PRICES_PRECISION;\n }\n\n withdrawalLimit_ = userSupply_ > withdrawalLimit_ ? userSupply_ - withdrawalLimit_ : 0;\n\n uint256 balanceAtLiquidity_ = _getLiquidityUnderlyingBalance();\n\n return balanceAtLiquidity_ > withdrawalLimit_ ? withdrawalLimit_ : balanceAtLiquidity_;\n }\n\n /// @dev Calculates new token exchange price based on the current liquidity exchange price `newLiquidityExchangePrice_` and rewards rate.\n /// @param newLiquidityExchangePrice_ new (current) liquidity exchange price\n function _calculateNewTokenExchangePrice(\n uint256 newLiquidityExchangePrice_\n ) internal view returns (uint256 newTokenExchangePrice_, bool rewardsEnded_) {\n uint256 oldTokenExchangePrice_ = _tokenExchangePrice;\n uint256 oldLiquidityExchangePrice_ = _liquidityExchangePrice;\n\n if (newLiquidityExchangePrice_ < oldLiquidityExchangePrice_) {\n // liquidity exchange price should only ever increase. If not, something went wrong and avoid\n // proceeding with unknown outcome.\n revert FluidLendingError(ErrorTypes.fToken__LiquidityExchangePriceUnexpected);\n }\n\n uint256 totalReturnInPercent_; // rewardsRateInPercent + liquidityReturnInPercent\n if (_rewardsActive) {\n {\n // get rewards rate per year\n // only trigger call to rewardsRateModel if rewards are actually active to save gas\n uint256 rewardsRate_;\n uint256 rewardsStartTime_;\n (rewardsRate_, rewardsEnded_, rewardsStartTime_) = _rewardsRateModel.getRate(\n // use old tokenExchangeRate to calculate the total assets input for the rewards rate\n (oldTokenExchangePrice_ * totalSupply()) / EXCHANGE_PRICES_PRECISION\n );\n\n if (rewardsRate_ > MAX_REWARDS_RATE || rewardsEnded_) {\n // rewardsRate is capped, if it is bigger > MAX_REWARDS_RATE, then the rewardsRateModel\n // is configured wrongly (which should not be possible). Setting rewards to 0 in that case here.\n rewardsRate_ = 0;\n }\n\n uint256 lastUpdateTimestamp_ = _lastUpdateTimestamp;\n if (lastUpdateTimestamp_ < rewardsStartTime_) {\n // if last update was before the rewards started, make sure rewards actually only accrue\n // from the actual rewards start time, not from the last update timestamp to avoid overpayment.\n lastUpdateTimestamp_ = rewardsStartTime_;\n\n // Note: overpayment for block.timestamp being > rewards end time does not happen because\n // rewardsRate_ is forced 0 then.\n }\n\n // calculate rewards return in percent: (rewards_rate * time passed) / seconds_in_a_year.\n unchecked {\n // rewardsRate * timeElapsed / SECONDS_PER_YEAR.\n // no safe checks needed here because timeElapsed can not underflow,\n // rewardsRate is in 1e12 at max value being MAX_REWARDS_RATE = 25e12\n // max value would be 25e12 * 8589934591 / 31536000 (with buffers) = 6.8e15\n totalReturnInPercent_ =\n (rewardsRate_ * (block.timestamp - lastUpdateTimestamp_)) /\n SECONDS_PER_YEAR;\n }\n }\n }\n\n unchecked {\n // calculate liquidityReturnInPercent: (newLiquidityExchangePrice_ - oldLiquidityExchangePrice_) / oldLiquidityExchangePrice_.\n // and add it to totalReturnInPercent_ that already holds rewardsRateInPercent_.\n // max value (in absolute extreme unrealistic case) would be: 6.8e15 + (((max uint64 - 1e12) * 1e12) / 1e12) = 1.845e19\n // oldLiquidityExchangePrice_ can not be 0, minimal value is 1e12. subtraction can not underflow because new exchange price\n // can only be >= oldLiquidityExchangePrice_.\n totalReturnInPercent_ +=\n ((newLiquidityExchangePrice_ - oldLiquidityExchangePrice_) * 1e14) /\n oldLiquidityExchangePrice_;\n }\n\n // newTokenExchangePrice_ = oldTokenExchangePrice_ + oldTokenExchangePrice_ * totalReturnInPercent_\n newTokenExchangePrice_ = oldTokenExchangePrice_ + ((oldTokenExchangePrice_ * totalReturnInPercent_) / 1e14); // divided by 100% (1e14)\n }\n\n /// @dev calculates new exchange prices, updates values in storage and returns new tokenExchangePrice (with reward rates)\n function _updateRates(\n uint256 liquidityExchangePrice_,\n bool forceUpdateStorage_\n ) internal returns (uint256 tokenExchangePrice_) {\n bool rewardsEnded_;\n (tokenExchangePrice_, rewardsEnded_) = _calculateNewTokenExchangePrice(liquidityExchangePrice_);\n if (_rewardsActive || forceUpdateStorage_) {\n // Solidity will NOT cause a revert if values are too big to fit max uint type size. Explicitly check before\n // writing to storage. Also see https://github.com/ethereum/solidity/issues/10195.\n if (tokenExchangePrice_ > type(uint64).max) {\n revert FluidLendingError(ErrorTypes.fToken__ExchangePriceOverflow);\n }\n\n _tokenExchangePrice = uint64(tokenExchangePrice_);\n _liquidityExchangePrice = uint64(liquidityExchangePrice_);\n _lastUpdateTimestamp = uint40(block.timestamp);\n\n emit LogUpdateRates(tokenExchangePrice_, liquidityExchangePrice_);\n }\n\n if (rewardsEnded_) {\n // set rewardsActive flag to false to save gas for all future exchange prices calculations,\n // without having to explicitly require setting `updateRewards` to address zero.\n // Note that it would be fine that even the current tx does not update exchange prices in storage,\n // because if rewardsEnded_ is true, rewardsRate_ must be 0, so the only yield is from LIQUIDITY.\n // But to be extra safe, writing to storage in that one case too before setting _rewardsActive to false.\n _rewardsActive = false;\n }\n\n return tokenExchangePrice_;\n }\n\n /// @dev splits a bytes signature `sig` into `v`, `r`, `s`.\n /// Taken from https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html\n function _splitSignature(bytes memory sig) internal pure returns (uint8 v, bytes32 r, bytes32 s) {\n require(sig.length == 65);\n\n assembly {\n // first 32 bytes, after the length prefix.\n r := mload(add(sig, 32))\n // second 32 bytes.\n s := mload(add(sig, 64))\n // final byte (first byte of the next 32 bytes).\n v := byte(0, mload(add(sig, 96)))\n }\n\n return (v, r, s);\n }\n\n /// @dev Deposit `assets_` amount of tokens to Liquidity\n /// @param assets_ The amount of tokens to deposit\n /// @param liquidityCallbackData_ callback data passed to Liquidity for `liquidityCallback`\n /// @return exchangePrice_ liquidity exchange price for token\n function _depositToLiquidity(\n uint256 assets_,\n bytes memory liquidityCallbackData_\n ) internal virtual returns (uint256 exchangePrice_) {\n // @dev Note: Although there might be some small difference between the `assets_` amount and the actual amount\n // accredited at Liquidity due to BigMath rounding down, this amount is so small that it can be ignored.\n // because of BigMath precision of 7.2057594e16 for a coefficient size of 56, it would require >72 trillion DAI\n // to \"benefit\" 1 DAI in additional shares minted. Considering gas cost + APR per second, this ensures such\n // a manipulation attempt becomes extremely unlikely.\n\n // send funds to Liquidity protocol to generate yield\n (exchangePrice_, ) = LIQUIDITY.operate(\n address(ASSET),\n SafeCast.toInt256(assets_),\n 0,\n address(0),\n address(0),\n liquidityCallbackData_ // callback data. -> \"from\" for transferFrom in `liquidityCallback`\n );\n }\n\n /// @dev Withdraw `assets_` amount of tokens from Liquidity directly to `receiver_`\n /// @param assets_ The amount of tokens to withdraw\n /// @param receiver_ the receiver address of withdraw amount\n /// @return exchangePrice_ liquidity exchange price for token\n function _withdrawFromLiquidity(\n uint256 assets_,\n address receiver_\n ) internal virtual returns (uint256 exchangePrice_) {\n // @dev See similar comment in `_depositToLiquidity()` regarding burning a tiny bit of additional shares here\n // because of inaccuracies in Liquidity userSupply BigMath being rounded down.\n\n // get funds back from Liquidity protocol to send to the user\n (exchangePrice_, ) = LIQUIDITY.operate(\n address(ASSET),\n -SafeCast.toInt256(assets_),\n 0,\n receiver_,\n address(0),\n new bytes(0) // callback data -> withdraw doesn't trigger a callback\n );\n }\n\n /// @dev deposits `assets_` into liquidity and mints shares for `receiver_`. Returns amount of `sharesMinted_`.\n function _executeDeposit(\n uint256 assets_,\n address receiver_,\n bytes memory liquidityCallbackData_\n ) internal virtual validAddress(receiver_) returns (uint256 sharesMinted_) {\n // send funds to Liquidity protocol to generate yield -> returns updated liquidityExchangePrice\n uint256 tokenExchangePrice_ = _depositToLiquidity(assets_, liquidityCallbackData_);\n\n // update the exchange prices\n tokenExchangePrice_ = _updateRates(tokenExchangePrice_, false);\n\n // calculate the shares to mint\n // not using previewDeposit here because we just got newTokenExchangePrice_\n sharesMinted_ = (assets_ * EXCHANGE_PRICES_PRECISION) / tokenExchangePrice_;\n\n if (sharesMinted_ == 0) {\n revert FluidLendingError(ErrorTypes.fToken__DepositInsignificant);\n }\n\n _mint(receiver_, sharesMinted_);\n\n emit Deposit(msg.sender, receiver_, assets_, sharesMinted_);\n }\n\n /// @dev withdraws `assets_` from liquidity to `receiver_` and burns shares from `owner_`.\n /// Returns amount of `sharesBurned_`.\n /// requires nonReentrant! modifier on calling method otherwise ERC777s could reenter!\n function _executeWithdraw(\n uint256 assets_,\n address receiver_,\n address owner_\n ) internal virtual validAddress(receiver_) returns (uint256 sharesBurned_) {\n // burn shares for assets_ amount: assets_ * EXCHANGE_PRICES_PRECISION / updatedTokenTexchangePrice. Rounded up.\n // Note to be extra safe we do the shares burn before the withdrawFromLiquidity, even though that would return the\n // updated liquidityExchangePrice and thus save gas.\n sharesBurned_ = assets_.mulDivUp(EXCHANGE_PRICES_PRECISION, _updateRates(_getLiquidityExchangePrice(), false));\n\n /*\n The `mulDivUp` function is designed to round up the result of multiplication followed by division. \n Given non-zero `assets_` and the rounding-up behavior of this function, `sharesBurned_` will always \n be at least 1 if there's any remainder in the division.\n Thus, if `assets_` is non-zero, `sharesBurned_` can never be 0. The nature of the function ensures \n that even the smallest fractional result (greater than 0) will be rounded up to 1. Hence, there's no need \n to check for a rounding error that results in 0.\n Furthermore, if `assets_` was 0, an error 'UserModule__OperateAmountsZero' would already have been thrown \n during the `operate` function, ensuring the contract never reaches this point with a zero `assets_` value.\n Note: If ever the logic or the function behavior changes in the future, this assertion may need to be reconsidered.\n */\n\n _burn(owner_, sharesBurned_);\n\n // withdraw from liquidity directly to _receiver.\n _withdrawFromLiquidity(assets_, receiver_);\n\n emit Withdraw(msg.sender, receiver_, owner_, assets_, sharesBurned_);\n }\n}\n\n/// @notice fToken view methods. Implements view methods for ERC4626 compatibility\nabstract contract fTokenViews is fTokenCore {\n using FixedPointMathLib for uint256;\n\n /// @inheritdoc IFToken\n function getData()\n public\n view\n returns (\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IFluidLendingRewardsRateModel lendingRewardsRateModel_,\n IAllowanceTransfer permit2_,\n address rebalancer_,\n bool rewardsActive_,\n uint256 liquidityBalance_,\n uint256 liquidityExchangePrice_,\n uint256 tokenExchangePrice_\n )\n {\n liquidityExchangePrice_ = _getLiquidityExchangePrice();\n\n bool rewardsEnded_;\n (tokenExchangePrice_, rewardsEnded_) = _calculateNewTokenExchangePrice(liquidityExchangePrice_);\n\n return (\n LIQUIDITY,\n LENDING_FACTORY,\n _rewardsRateModel,\n PERMIT2,\n _rebalancer,\n _rewardsActive && !rewardsEnded_,\n _getLiquidityBalance(),\n liquidityExchangePrice_,\n tokenExchangePrice_\n );\n }\n\n /// @inheritdoc IERC4626\n function asset() public view virtual override returns (address) {\n return address(ASSET);\n }\n\n /// @inheritdoc IERC4626\n function totalAssets() public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return\n // all the underlying tokens are stored in Liquidity contract at all times\n (tokenExchangePrice_ * totalSupply()) / EXCHANGE_PRICES_PRECISION;\n }\n\n /// @inheritdoc IERC4626\n function convertToShares(uint256 assets_) public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return assets_.mulDivDown(EXCHANGE_PRICES_PRECISION, tokenExchangePrice_);\n }\n\n /// @inheritdoc IERC4626\n function convertToAssets(uint256 shares_) public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return shares_.mulDivDown(tokenExchangePrice_, EXCHANGE_PRICES_PRECISION);\n }\n\n /// @inheritdoc IERC4626\n /// @notice returned amount might be slightly different from actual amount at execution.\n function previewDeposit(uint256 assets_) public view virtual override returns (uint256) {\n return convertToShares(assets_);\n }\n\n /// @inheritdoc IERC4626\n function previewMint(uint256 shares_) public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return shares_.mulDivUp(tokenExchangePrice_, EXCHANGE_PRICES_PRECISION);\n }\n\n /// @inheritdoc IERC4626\n function previewWithdraw(uint256 assets_) public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return assets_.mulDivUp(EXCHANGE_PRICES_PRECISION, tokenExchangePrice_);\n }\n\n /// @inheritdoc IERC4626\n /// @notice returned amount might be slightly different from actual amount at execution.\n function previewRedeem(uint256 shares_) public view virtual override returns (uint256) {\n return convertToAssets(shares_);\n }\n\n /*//////////////////////////////////////////////////////////////\n DEPOSIT/WITHDRAWAL LIMIT LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n function maxDeposit(address) public view virtual override returns (uint256) {\n // read total supplyInterest_ for the token at Liquidity and convert from BigMath\n uint256 supplyInterest_ = LIQUIDITY.readFromStorage(LIQUIDITY_TOTAL_AMOUNTS_SLOT) & LiquidityCalcs.X64;\n supplyInterest_ =\n (supplyInterest_ >> LiquidityCalcs.DEFAULT_EXPONENT_SIZE) <<\n (supplyInterest_ & LiquidityCalcs.DEFAULT_EXPONENT_MASK);\n\n unchecked {\n // normalize from raw\n supplyInterest_ = (supplyInterest_ * _getLiquidityExchangePrice()) / EXCHANGE_PRICES_PRECISION;\n // compare against hardcoded max possible value for total supply considering BigMath rounding down:\n // type(int128).max) after BigMath rounding (first 56 bits precision, then 71 bits getting set to 0)\n // so 1111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000\n // = 170141183460469229370504062281061498880. using minus 1\n if (supplyInterest_ > 170141183460469229370504062281061498879) {\n return 0;\n }\n // type(int128).max is the maximum interactable amount at Liquidity. But also total token amounts\n // must not overflow type(int128).max, so max depositable is type(int128).max - totalSupply.\n return uint256(uint128(type(int128).max)) - supplyInterest_;\n }\n }\n\n /// @inheritdoc IERC4626\n function maxMint(address) public view virtual override returns (uint256) {\n return convertToShares(maxDeposit(address(0)));\n }\n\n /// @inheritdoc IERC4626\n function maxWithdraw(address owner_) public view virtual override returns (uint256) {\n uint256 maxWithdrawableAtLiquidity_ = _getLiquidityWithdrawable();\n uint256 ownerBalance_ = convertToAssets(balanceOf(owner_));\n return maxWithdrawableAtLiquidity_ < ownerBalance_ ? maxWithdrawableAtLiquidity_ : ownerBalance_;\n }\n\n /// @inheritdoc IERC4626\n function maxRedeem(address owner_) public view virtual override returns (uint256) {\n uint256 maxWithdrawableAtLiquidity_ = convertToShares(_getLiquidityWithdrawable());\n uint256 ownerBalance_ = balanceOf(owner_);\n return maxWithdrawableAtLiquidity_ < ownerBalance_ ? maxWithdrawableAtLiquidity_ : ownerBalance_;\n }\n\n /// @inheritdoc IFToken\n function minDeposit() public view returns (uint256) {\n uint256 minBigMathRounding_ = 1 <<\n (LIQUIDITY.readFromStorage(LIQUIDITY_TOTAL_AMOUNTS_SLOT) & LiquidityCalcs.DEFAULT_EXPONENT_MASK); // 1 << total supply exponent\n uint256 previewMint_ = previewMint(1); // rounds up\n return minBigMathRounding_ > previewMint_ ? minBigMathRounding_ : previewMint_;\n }\n}\n\n/// @notice fToken admin related methods. fToken admins are Lending Factory auths. Possible actions are\n/// updating rewards, funding rewards, and rescuing any stuck funds (fToken contract itself never holds any funds).\nabstract contract fTokenAdmin is fTokenCore, fTokenViews {\n /// @dev checks if `msg.sender` is an allowed auth at LendingFactory. internal method instead of modifier\n /// to reduce bytecode size.\n function _checkIsLendingFactoryAuth() internal view {\n if (!LENDING_FACTORY.isAuth(msg.sender)) {\n revert FluidLendingError(ErrorTypes.fToken__Unauthorized);\n }\n }\n\n /// @inheritdoc IFTokenAdmin\n function updateRewards(IFluidLendingRewardsRateModel rewardsRateModel_) external {\n _checkIsLendingFactoryAuth();\n\n // @dev no check for address zero needed here, as that is actually explicitly checked where _rewardsRateModel\n // is used. In fact it is beneficial to set _rewardsRateModel to address zero when there are no rewards.\n\n // apply current rewards rate before updating to new one\n updateRates();\n\n _rewardsRateModel = rewardsRateModel_;\n\n // set flag _rewardsActive\n _rewardsActive = address(rewardsRateModel_) != address(0);\n\n emit LogUpdateRewards(rewardsRateModel_);\n }\n\n /// @inheritdoc IFTokenAdmin\n function rebalance() external payable virtual nonReentrant returns (uint256 assets_) {\n if (msg.sender != _rebalancer) {\n revert FluidLendingError(ErrorTypes.fToken__NotRebalancer);\n }\n if (msg.value > 0) {\n revert FluidLendingError(ErrorTypes.fToken__NotNativeUnderlying);\n }\n // calculating difference in assets. if liquidity balance is bigger it'll throw which is an expected behaviour\n assets_ = totalAssets() - _getLiquidityBalance();\n // send funds to Liquidity protocol\n uint256 liquidityExchangePrice_ = _depositToLiquidity(assets_, abi.encode(msg.sender));\n\n // update the exchange prices, always updating on storage\n _updateRates(liquidityExchangePrice_, true);\n\n // no shares are minted when funding fToken contract for rewards\n\n emit LogRebalance(assets_);\n }\n\n /// @inheritdoc IFTokenAdmin\n function updateRebalancer(address newRebalancer_) public validAddress(newRebalancer_) {\n _checkIsLendingFactoryAuth();\n\n _rebalancer = newRebalancer_;\n\n emit LogUpdateRebalancer(newRebalancer_);\n }\n\n /// @inheritdoc IFTokenAdmin\n function updateRates() public returns (uint256 tokenExchangePrice_, uint256 liquidityExchangePrice_) {\n liquidityExchangePrice_ = _getLiquidityExchangePrice();\n tokenExchangePrice_ = _updateRates(liquidityExchangePrice_, true);\n }\n\n /// @inheritdoc IFTokenAdmin\n //\n // @dev this contract never holds any funds:\n // -> deposited funds are directly sent to Liquidity.\n // -> rewards are also stored at Liquidity.\n function rescueFunds(address token_) external virtual nonReentrant {\n _checkIsLendingFactoryAuth();\n SafeTransfer.safeTransfer(address(token_), address(LIQUIDITY), IERC20(token_).balanceOf(address(this)));\n emit LogRescueFunds(token_);\n }\n}\n\n/// @notice fToken public executable actions: deposit, mint, mithdraw and redeem.\n/// All actions are optionally also available with an additional param to limit the maximum slippage, e.g. maximum\n/// assets used for minting x amount of shares.\nabstract contract fTokenActions is fTokenCore, fTokenViews {\n /// @dev reverts if `amount_` is < `minAmountOut_`. Used to reduce bytecode size.\n function _revertIfBelowMinAmountOut(uint256 amount_, uint256 minAmountOut_) internal pure {\n if (amount_ < minAmountOut_) {\n revert FluidLendingError(ErrorTypes.fToken__MinAmountOut);\n }\n }\n\n /// @dev reverts if `amount_` is > `maxAmount_`. Used to reduce bytecode size.\n function _revertIfAboveMaxAmount(uint256 amount_, uint256 maxAmount_) internal pure {\n if (amount_ > maxAmount_) {\n revert FluidLendingError(ErrorTypes.fToken__MaxAmount);\n }\n }\n\n /*//////////////////////////////////////////////////////////////\n DEPOSIT\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n /// @notice If `assets_` equals uint256.max then the whole balance of `msg.sender` is deposited.\n /// `assets_` must at least be `minDeposit()` amount; reverts `fToken__DepositInsignificant()` if not.\n /// Recommended to use `deposit()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return shares_ actually minted shares\n function deposit(\n uint256 assets_,\n address receiver_\n ) public virtual override nonReentrant returns (uint256 shares_) {\n if (assets_ == type(uint256).max) {\n assets_ = ASSET.balanceOf(msg.sender);\n }\n\n // @dev transfer of tokens from `msg.sender` to liquidity contract happens via `liquidityCallback`\n shares_ = _executeDeposit(assets_, receiver_, abi.encode(msg.sender));\n }\n\n /// @notice same as {fToken-deposit} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached\n function deposit(uint256 assets_, address receiver_, uint256 minAmountOut_) external returns (uint256 shares_) {\n shares_ = deposit(assets_, receiver_);\n _revertIfBelowMinAmountOut(shares_, minAmountOut_);\n }\n\n /*//////////////////////////////////////////////////////////////\n MINT \n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n /// @notice If `shares_` equals uint256.max then the whole balance of `msg.sender` is deposited.\n /// `shares_` must at least be `minMint()` amount; reverts `fToken__DepositInsignificant()` if not.\n /// Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.\n /// Recommended to use `deposit()` over mint because it is more gas efficient and less likely to revert.\n /// Recommended to use `mint()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return assets_ deposited assets amount\n function mint(uint256 shares_, address receiver_) public virtual override nonReentrant returns (uint256 assets_) {\n if (shares_ == type(uint256).max) {\n assets_ = ASSET.balanceOf(msg.sender);\n } else {\n // No need to check for rounding error, previewMint rounds up.\n assets_ = previewMint(shares_);\n }\n\n // @dev transfer of tokens from `msg.sender` to liquidity contract happens via `liquidityCallback`\n\n _executeDeposit(assets_, receiver_, abi.encode(msg.sender));\n }\n\n /// @notice same as {fToken-mint} but with an additional setting for maximum assets input amount.\n /// reverts with `fToken__MaxAmount()` if `maxAssets_` of assets is surpassed to mint `shares_`.\n function mint(uint256 shares_, address receiver_, uint256 maxAssets_) external returns (uint256 assets_) {\n assets_ = mint(shares_, receiver_);\n _revertIfAboveMaxAmount(assets_, maxAssets_);\n }\n\n /*//////////////////////////////////////////////////////////////\n WITHDRAW\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n /// @notice If `assets_` equals uint256.max then the whole fToken balance of `owner_` is withdrawn. This does not\n /// consider withdrawal limit at Liquidity so best to check with `maxWithdraw()` before.\n /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.\n /// Recommended to use `withdraw()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return shares_ burned shares\n function withdraw(\n uint256 assets_,\n address receiver_,\n address owner_\n ) public virtual override nonReentrant returns (uint256 shares_) {\n if (assets_ == type(uint256).max) {\n assets_ = previewRedeem(balanceOf(owner_));\n }\n shares_ = _executeWithdraw(assets_, receiver_, owner_);\n\n if (msg.sender != owner_) {\n _spendAllowance(owner_, msg.sender, shares_);\n }\n }\n\n /// @notice same as {fToken-withdraw} but with an additional setting for maximum shares burned.\n /// reverts with `fToken__MaxAmount()` if `maxSharesBurn_` of shares burned is surpassed.\n function withdraw(\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_\n ) external returns (uint256 shares_) {\n shares_ = withdraw(assets_, receiver_, owner_);\n _revertIfAboveMaxAmount(shares_, maxSharesBurn_);\n }\n\n /*//////////////////////////////////////////////////////////////\n REDEEM\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n /// @notice If `shares_` equals uint256.max then the whole balance of `owner_` is withdrawn.This does not\n /// consider withdrawal limit at Liquidity so best to check with `maxRedeem()` before.\n /// Recommended to use `withdraw()` over redeem because it is more gas efficient and can set specific amount.\n /// Recommended to use `redeem()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return assets_ withdrawn assets amount\n function redeem(\n uint256 shares_,\n address receiver_,\n address owner_\n ) public virtual override nonReentrant returns (uint256 assets_) {\n if (shares_ == type(uint256).max) {\n shares_ = balanceOf(owner_);\n }\n\n assets_ = previewRedeem(shares_);\n\n uint256 burnedShares_ = _executeWithdraw(assets_, receiver_, owner_);\n\n if (msg.sender != owner_) {\n _spendAllowance(owner_, msg.sender, burnedShares_);\n }\n }\n\n /// @notice same as {fToken-redeem} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of assets is not reached.\n function redeem(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_\n ) external returns (uint256 assets_) {\n assets_ = redeem(shares_, receiver_, owner_);\n _revertIfBelowMinAmountOut(assets_, minAmountOut_);\n }\n}\n\n/// @notice fTokens support EIP-2612 permit approvals via signature so this contract implements\n/// withdrawals (withdraw / redeem) with signature used for approval of the fToken shares.\nabstract contract fTokenEIP2612Withdrawals is fTokenActions {\n /// @dev creates `sharesToPermit_` allowance for `owner_` via EIP2612 `deadline_` and `signature_`\n function _allowViaPermitEIP2612(\n address owner_,\n uint256 sharesToPermit_,\n uint256 deadline_,\n bytes calldata signature_\n ) internal {\n (uint8 v_, bytes32 r_, bytes32 s_) = _splitSignature(signature_);\n // spender = msg.sender\n permit(owner_, msg.sender, sharesToPermit_, deadline_, v_, r_, s_);\n }\n\n /// @notice withdraw amount of `assets_` with ERC-2612 permit signature for fToken approval.\n /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.\n /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.\n /// allowance via signature (`sharesToPermit_`) should cover `previewWithdraw(assets_)` plus a little buffer to avoid revert.\n /// Inherent trust assumption that `msg.sender` will set `receiver_` and `maxSharesBurn_` as `owner_` intends\n /// (which is always the case when giving allowance to some spender).\n /// @param sharesToPermit_ shares amount to use for EIP2612 permit(). Should cover `previewWithdraw(assets_)` + small buffer.\n /// @param assets_ amount of assets to withdraw\n /// @param receiver_ receiver of withdrawn assets\n /// @param owner_ owner to withdraw from (must be signature signer)\n /// @param maxSharesBurn_ maximum accepted amount of shares burned\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for ERC-2612 permit\n /// @return shares_ burned shares amount\n function withdrawWithSignature(\n uint256 sharesToPermit_,\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_,\n uint256 deadline_,\n bytes calldata signature_\n ) external virtual nonReentrant returns (uint256 shares_) {\n if (msg.sender == owner_) {\n // no sense in operating with permit if msg.sender is owner. should call normal `withdraw()` instead.\n revert FluidLendingError(ErrorTypes.fToken__PermitFromOwnerCall);\n }\n\n // create allowance through signature_\n _allowViaPermitEIP2612(owner_, sharesToPermit_, deadline_, signature_);\n\n // execute withdraw to get shares_ to spend amount\n shares_ = _executeWithdraw(assets_, receiver_, owner_);\n\n _revertIfAboveMaxAmount(shares_, maxSharesBurn_);\n\n _spendAllowance(owner_, msg.sender, shares_);\n }\n\n /// @notice redeem amount of `shares_` with ERC-2612 permit signature for fToken approval.\n /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.\n /// Note there might be tiny inaccuracies between requested `shares_` to redeem and actually burned shares.\n /// allowance via signature must cover `shares_` plus a tiny buffer.\n /// Inherent trust assumption that `msg.sender` will set `receiver_` and `minAmountOut_` as `owner_` intends\n /// (which is always the case when giving allowance to some spender).\n /// Recommended to use `withdraw()` over redeem because it is more gas efficient and can set specific amount.\n /// @param shares_ amount of shares to redeem\n /// @param receiver_ receiver of withdrawn assets\n /// @param owner_ owner to withdraw from (must be signature signer)\n /// @param minAmountOut_ minimum accepted amount of assets withdrawn\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for ERC-2612 permit\n /// @return assets_ withdrawn assets amount\n function redeemWithSignature(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_,\n uint256 deadline_,\n bytes calldata signature_\n ) external virtual nonReentrant returns (uint256 assets_) {\n if (msg.sender == owner_) {\n // no sense in operating with permit if msg.sender is owner. should call normal `redeem()` instead.\n revert FluidLendingError(ErrorTypes.fToken__PermitFromOwnerCall);\n }\n\n assets_ = previewRedeem(shares_);\n _revertIfBelowMinAmountOut(assets_, minAmountOut_);\n\n // create allowance through signature_\n _allowViaPermitEIP2612(owner_, shares_, deadline_, signature_);\n\n // execute withdraw to get actual shares to spend amount\n uint256 sharesToSpend_ = _executeWithdraw(assets_, receiver_, owner_);\n\n _spendAllowance(owner_, msg.sender, sharesToSpend_);\n }\n}\n\n/// @notice implements fTokens support for deposit / mint via EIP-2612 permit.\n/// @dev methods revert if underlying asset does not support EIP-2612.\nabstract contract fTokenEIP2612Deposits is fTokenActions {\n /// @notice deposit `assets_` amount with EIP-2612 Permit2 signature for underlying asset approval.\n /// IMPORTANT: This will revert if the underlying `asset()` does not support EIP-2612.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached.\n /// `assets_` must at least be `minDeposit()` amount; reverts `fToken__DepositInsignificant()` if not.\n /// @param assets_ amount of assets to deposit\n /// @param receiver_ receiver of minted fToken shares\n /// @param minAmountOut_ minimum accepted amount of shares minted\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for EIP-2612 Permit\n /// @return shares_ amount of minted shares\n function depositWithSignatureEIP2612(\n uint256 assets_,\n address receiver_,\n uint256 minAmountOut_,\n uint256 deadline_,\n bytes calldata signature_\n ) external returns (uint256 shares_) {\n // create allowance through signature_ and spend it\n (uint8 v_, bytes32 r_, bytes32 s_) = _splitSignature(signature_);\n\n // EIP-2612 permit for underlying asset from owner (msg.sender) to spender (this contract)\n IERC20Permit(address(ASSET)).permit(msg.sender, address(this), assets_, deadline_, v_, r_, s_);\n\n // deposit() includes nonReentrant modifier which is enough to have from this point forward\n shares_ = deposit(assets_, receiver_);\n _revertIfBelowMinAmountOut(shares_, minAmountOut_);\n }\n\n /// @notice mint amount of `shares_` with EIP-2612 Permit signature for underlying asset approval.\n /// IMPORTANT: This will revert if the underlying `asset()` does not support EIP-2612.\n /// Signature should approve a little bit more than expected assets amount (`previewMint()`) to avoid reverts.\n /// `shares_` must at least be `minMint()` amount; reverts with `fToken__DepositInsignificant()` if not.\n /// Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.\n /// Recommended to use `deposit()` over mint because it is more gas efficient and less likely to revert.\n /// @param shares_ amount of shares to mint\n /// @param receiver_ receiver of minted fToken shares\n /// @param maxAssets_ maximum accepted amount of assets used as input to mint `shares_`\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for EIP-2612 Permit\n /// @return assets_ deposited assets amount\n function mintWithSignatureEIP2612(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_,\n uint256 deadline_,\n bytes calldata signature_\n ) external returns (uint256 assets_) {\n assets_ = previewMint(shares_);\n\n // create allowance through signature_ and spend it\n (uint8 v_, bytes32 r_, bytes32 s_) = _splitSignature(signature_);\n\n // EIP-2612 permit for underlying asset from owner (msg.sender) to spender (this contract)\n IERC20Permit(address(ASSET)).permit(msg.sender, address(this), assets_, deadline_, v_, r_, s_);\n\n // mint() includes nonReentrant modifier which is enough to have from this point forward\n assets_ = mint(shares_, receiver_);\n _revertIfAboveMaxAmount(assets_, maxAssets_);\n }\n}\n\n/// @notice implements fTokens support for deposit / mint via Permit2 signature.\nabstract contract fTokenPermit2Deposits is fTokenActions {\n /// @inheritdoc IFToken\n function depositWithSignature(\n uint256 assets_,\n address receiver_,\n uint256 minAmountOut_,\n IAllowanceTransfer.PermitSingle calldata permit_,\n bytes calldata signature_\n ) external nonReentrant returns (uint256 shares_) {\n // give allowance to address(this) via Permit2 signature -> to spend allowance in LiquidityCallback\n // to transfer funds directly from msg.sender to liquidity\n PERMIT2.permit(\n // owner - Who signed the permit and also holds the tokens\n // @dev Note if this is modified to not be msg.sender, extra steps would be needed for security!\n // the caller could use this signature and deposit to the balance of receiver_, which could be set to any address,\n // because it is not included in the signature. Use permitWitnessTransferFrom in that case. Same for `minAmountOut_`.\n msg.sender,\n permit_, // permit message\n signature_ // packed signature of signing the EIP712 hash of `permit_`\n );\n\n // @dev transfer of tokens from `msg.sender` to liquidity contract happens via `liquidityCallback`\n\n shares_ = _executeDeposit(assets_, receiver_, abi.encode(true, msg.sender));\n _revertIfBelowMinAmountOut(shares_, minAmountOut_);\n }\n\n /// @inheritdoc IFToken\n function mintWithSignature(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_,\n IAllowanceTransfer.PermitSingle calldata permit_,\n bytes calldata signature_\n ) external nonReentrant returns (uint256 assets_) {\n assets_ = previewMint(shares_);\n _revertIfAboveMaxAmount(assets_, maxAssets_);\n\n // give allowance to address(this) via Permit2 PermitSingle. to spend allowance in LiquidityCallback\n // to transfer funds directly from msg.sender to liquidity\n PERMIT2.permit(\n // owner - Who signed the permit and also holds the tokens\n // @dev Note if this is modified to not be msg.sender, extra steps would be needed for security!\n // the caller could use this signature and deposit to the balance of receiver_, which could be set to any address,\n // because it is not included in the signature. Use permitWitnessTransferFrom in that case. Same for `minAmountOut_`.\n msg.sender,\n permit_, // permit message\n signature_ // packed signature of signing the EIP712 hash of `permit_`\n );\n\n // @dev transfer of tokens from `msg.sender` to liquidity contract happens via `liquidityCallback`\n\n _executeDeposit(assets_, receiver_, abi.encode(true, msg.sender));\n }\n}\n\n/// @title Fluid fToken (Lending with interest)\n/// @notice fToken is a token that can be used to supply liquidity to the Fluid Liquidity pool and earn interest for doing so.\n/// The fToken is backed by the underlying balance and can be redeemed for the underlying token at any time.\n/// The interest is earned via Fluid Liquidity, e.g. because borrowers pay a borrow rate on it. In addition, fTokens may also\n/// have active rewards going on that count towards the earned yield for fToken holders.\n/// @dev The fToken implements the ERC20 and ERC4626 standard, which means it can be transferred, minted and burned.\n/// The fToken supports EIP-2612 permit approvals via signature.\n/// The fToken implements withdrawals via EIP-2612 permits and deposits with Permit2 or EIP-2612 (if underlying supports it) signatures.\n/// fTokens are not upgradeable.\n/// @dev For view methods / accessing data, use the \"LendingResolver\" periphery contract.\n//\n// fTokens can only be deployed for underlying tokens that are listed at Liquidity (`_getLiquidityExchangePrice()` reverts\n// otherwise, which is called in the constructor).\ncontract fToken is fTokenAdmin, fTokenActions, fTokenEIP2612Withdrawals, fTokenPermit2Deposits, fTokenEIP2612Deposits {\n /// @param liquidity_ liquidity contract address\n /// @param lendingFactory_ lending factory contract address\n /// @param asset_ underlying token address\n constructor(\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IERC20 asset_\n ) Variables(liquidity_, lendingFactory_, asset_) {\n // set initial values for _liquidityExchangePrice, _tokenExchangePrice and _lastUpdateTimestamp\n _liquidityExchangePrice = uint64(_getLiquidityExchangePrice());\n _tokenExchangePrice = uint64(EXCHANGE_PRICES_PRECISION);\n _lastUpdateTimestamp = uint40(block.timestamp);\n }\n\n /// @inheritdoc IERC20Metadata\n function decimals() public view virtual override(ERC20, IERC20Metadata) returns (uint8) {\n return DECIMALS;\n }\n\n /// @inheritdoc IFToken\n function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external virtual override {\n if (msg.sender != address(LIQUIDITY) || token_ != address(ASSET) || _status != REENTRANCY_ENTERED) {\n // caller must be liquidity, token must match, and reentrancy status must be REENTRANCY_ENTERED\n revert FluidLendingError(ErrorTypes.fToken__Unauthorized);\n }\n\n // callback data can be a) an address only b) an address + transfer via permit2 flag set to true\n // for a) length will be 32, for b) length is 64\n if (data_.length == 32) {\n address from_ = abi.decode(data_, (address));\n\n // transfer `amount_` from `from_` (original deposit msg.sender) to liquidity contract\n SafeTransfer.safeTransferFrom(address(ASSET), from_, address(LIQUIDITY), amount_);\n } else {\n (bool isPermit2_, address from_) = abi.decode(data_, (bool, address));\n if (!isPermit2_) {\n // unexepcted liquidity callback data\n revert FluidLendingError(ErrorTypes.fToken__InvalidParams);\n }\n\n // transfer `amount_` from `from_` (original deposit msg.sender) to liquidity contract via PERMIT2\n PERMIT2.transferFrom(from_, address(LIQUIDITY), uint160(amount_), address(ASSET));\n }\n }\n}\n" }, "contracts/protocols/lending/fToken/nativeUnderlying/fTokenNativeUnderlying.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\nimport { FixedPointMathLib } from \"solmate/src/utils/FixedPointMathLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { SafeTransfer } from \"../../../../libraries/safeTransfer.sol\";\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { fTokenCore, fTokenAdmin, fToken } from \"../main.sol\";\n\nimport { IWETH9 } from \"../../interfaces/external/iWETH9.sol\";\nimport { IFluidLendingFactory } from \"../../interfaces/iLendingFactory.sol\";\nimport { IFTokenAdmin, IFTokenNativeUnderlying, IFToken } from \"../../interfaces/iFToken.sol\";\nimport { IFluidLiquidity } from \"../../../../liquidity/interfaces/iLiquidity.sol\";\n\n/// @dev overrides certain methods from the inherited fToken used as base contract to make them compatible with\n/// the native token being used as underlying.\nabstract contract fTokenNativeUnderlyingOverrides is fToken, IFTokenNativeUnderlying {\n using FixedPointMathLib for uint256;\n\n /// @inheritdoc IFTokenNativeUnderlying\n address public constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @dev gets asset address for liquidity slot links, overridden to set native token address\n function _getLiquiditySlotLinksAsset() internal view virtual override returns (address) {\n return NATIVE_TOKEN_ADDRESS;\n }\n\n /// @dev Gets current Liquidity underlying token balance\n function _getLiquidityUnderlyingBalance() internal view virtual override returns (uint256) {\n return address(LIQUIDITY).balance;\n }\n\n /// @inheritdoc IFTokenAdmin\n function rescueFunds(address token_) external virtual override(IFTokenAdmin, fTokenAdmin) nonReentrant {\n _checkIsLendingFactoryAuth();\n\n if (token_ == NATIVE_TOKEN_ADDRESS) {\n Address.sendValue(payable(address(LIQUIDITY)), address(this).balance);\n } else {\n SafeTransfer.safeTransfer(address(token_), address(LIQUIDITY), IERC20(token_).balanceOf(address(this)));\n }\n\n emit LogRescueFunds(token_);\n }\n\n /*//////////////////////////////////////////////////////////////\n REWARDS\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc fTokenAdmin\n function rebalance()\n external\n payable\n virtual\n override(IFTokenAdmin, fTokenAdmin)\n nonReentrant\n returns (uint256 assets_)\n {\n if (msg.sender != _rebalancer) {\n revert FluidLendingError(ErrorTypes.fToken__NotRebalancer);\n }\n // calculating difference in assets. if liquidity balance is bigger it'll throw which is an expected behaviour\n assets_ = totalAssets() - _getLiquidityBalance();\n\n if (msg.value < assets_) {\n assets_ = msg.value;\n } else if (msg.value > assets_) {\n // send back overfunded msg.value amount\n Address.sendValue(payable(msg.sender), msg.value - assets_);\n }\n\n // send funds to Liquidity protocol to generate yield\n uint256 liquidityExchangePrice_ = _depositToLiquidity(assets_, new bytes(0));\n\n // update the exchange prices, always updating on storage\n _updateRates(liquidityExchangePrice_, true);\n\n // no shares are minted when funding fToken contract for rewards\n\n emit LogRebalance(assets_);\n }\n\n /*//////////////////////////////////////////////////////////////\n DEPOSIT\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc fTokenCore\n function _depositToLiquidity(\n uint256 assets_,\n bytes memory liquidityCallbackData_\n ) internal virtual override returns (uint256 exchangePrice_) {\n // send funds to Liquidity protocol to generate yield, send along msg.value\n (exchangePrice_, ) = LIQUIDITY.operate{ value: assets_ }(\n NATIVE_TOKEN_ADDRESS, // deposit to Liquidity is always in native, also if user input is wrapped token\n SafeCast.toInt256(assets_),\n 0,\n address(0),\n address(0),\n liquidityCallbackData_ // callback data. -> \"from\" for transferFrom in `liquidityCallback`\n );\n }\n\n /// @inheritdoc fTokenCore\n function _executeDeposit(\n uint256 assets_,\n address receiver_,\n // liquidityCallbackData_ not needed for native transfer, sent along as msg.value. But used to recognize Permit2 transfers.\n bytes memory liquidityCallbackData_\n ) internal virtual override returns (uint256 sharesMinted_) {\n // transfer wrapped asset from user to this contract\n if (liquidityCallbackData_.length > 32) {\n // liquidityCallbackData_ with length > 32 can only be Permit2 as all others maximally encode from address\n PERMIT2.transferFrom(msg.sender, address(this), uint160(assets_), address(ASSET));\n } else {\n SafeTransfer.safeTransferFrom(address(ASSET), msg.sender, address(this), assets_);\n }\n\n // convert WETH to native underlying token\n IWETH9(address(ASSET)).withdraw(assets_);\n\n // super._executeDeposit includes check for validAddress receiver_\n return super._executeDeposit(assets_, receiver_, new bytes(0));\n }\n\n /// @dev deposits `msg.value` amount of native token into liquidity and mints shares for `receiver_`.\n /// Returns amount of `sharesMinted_`.\n function _executeDepositNative(address receiver_) internal virtual returns (uint256 sharesMinted_) {\n // super._executeDeposit includes check for validAddress receiver_\n return super._executeDeposit(msg.value, receiver_, new bytes(0));\n }\n\n /*//////////////////////////////////////////////////////////////\n WITHDRAW\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc fTokenCore\n function _withdrawFromLiquidity(\n uint256 assets_,\n address receiver_\n ) internal virtual override returns (uint256 exchangePrice_) {\n // get funds back from Liquidity protocol to send to the user\n (exchangePrice_, ) = LIQUIDITY.operate(\n NATIVE_TOKEN_ADDRESS, // withdraw from Liquidity is always in native, also if user output is wrapped token\n -SafeCast.toInt256(assets_),\n 0,\n receiver_,\n address(0),\n new bytes(0) // callback data -> withdraw doesn't trigger a callback\n );\n }\n\n /// @inheritdoc fTokenCore\n function _executeWithdraw(\n uint256 assets_,\n address receiver_,\n address owner_\n ) internal virtual override returns (uint256 sharesBurned_) {\n // super._executeWithdraw includes check for validAddress(receiver_)\n\n // withdraw from liquidity to this contract first to convert withdrawn native token to wrapped native for _receiver.\n sharesBurned_ = super._executeWithdraw(assets_, address(this), owner_);\n\n // convert received native underlying token to WETH and transfer to receiver_\n IWETH9(address(ASSET)).deposit{ value: assets_ }();\n SafeTransfer.safeTransfer(address(ASSET), receiver_, assets_);\n }\n\n /// @dev withdraws `assets_` from liquidity to `receiver_` and burns shares from `owner_`.\n /// Returns amount of `sharesBurned_`.\n function _executeWithdrawNative(\n uint256 assets_,\n address receiver_,\n address owner_\n ) internal virtual returns (uint256 sharesBurned_) {\n // super._executeWithdraw includes check for validAddress(receiver_)\n return super._executeWithdraw(assets_, receiver_, owner_);\n }\n}\n\n/// @notice implements deposit / mint / withdraw / redeem actions with Native token being used as interaction token.\nabstract contract fTokenNativeUnderlyingActions is fTokenNativeUnderlyingOverrides {\n /*//////////////////////////////////////////////////////////////\n DEPOSIT\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFTokenNativeUnderlying\n function depositNative(address receiver_) public payable nonReentrant returns (uint256 shares_) {\n shares_ = _executeDepositNative(receiver_);\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function depositNative(address receiver_, uint256 minAmountOut_) external payable returns (uint256 shares_) {\n shares_ = depositNative(receiver_);\n _revertIfBelowMinAmountOut(shares_, minAmountOut_);\n }\n\n /*//////////////////////////////////////////////////////////////\n MINT \n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFTokenNativeUnderlying\n function mintNative(uint256 shares_, address receiver_) public payable nonReentrant returns (uint256 assets_) {\n // No need to check for rounding error, previewMint rounds up.\n assets_ = previewMint(shares_);\n\n if (msg.value < assets_) {\n // not enough msg.value sent along to cover mint shares amount\n revert FluidLendingError(ErrorTypes.fTokenNativeUnderlying__TransferInsufficient);\n }\n\n _executeDepositNative(receiver_);\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function mintNative(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_\n ) external payable returns (uint256 assets_) {\n assets_ = mintNative(shares_, receiver_);\n _revertIfAboveMaxAmount(assets_, maxAssets_);\n }\n\n /*//////////////////////////////////////////////////////////////\n WITHDRAW\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFTokenNativeUnderlying\n function withdrawNative(\n uint256 assets_,\n address receiver_,\n address owner_\n ) public nonReentrant returns (uint256 shares_) {\n if (assets_ == type(uint256).max) {\n assets_ = previewRedeem(balanceOf(msg.sender));\n }\n\n shares_ = _executeWithdrawNative(assets_, receiver_, owner_);\n\n if (msg.sender != owner_) {\n _spendAllowance(owner_, msg.sender, shares_);\n }\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function withdrawNative(\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_\n ) external returns (uint256 shares_) {\n shares_ = withdrawNative(assets_, receiver_, owner_);\n _revertIfAboveMaxAmount(shares_, maxSharesBurn_);\n }\n\n /*//////////////////////////////////////////////////////////////\n REDEEM\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFTokenNativeUnderlying\n function redeemNative(\n uint256 shares_,\n address receiver_,\n address owner_\n ) public nonReentrant returns (uint256 assets_) {\n if (shares_ == type(uint256).max) {\n shares_ = balanceOf(msg.sender);\n }\n\n assets_ = previewRedeem(shares_);\n\n uint256 burnedShares_ = _executeWithdrawNative(assets_, receiver_, owner_);\n\n if (msg.sender != owner_) {\n _spendAllowance(owner_, msg.sender, burnedShares_);\n }\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function redeemNative(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_\n ) external returns (uint256 assets_) {\n assets_ = redeemNative(shares_, receiver_, owner_);\n _revertIfBelowMinAmountOut(assets_, minAmountOut_);\n }\n}\n\n/// @notice fTokens support EIP-2612 permit approvals via signature so withdrawals are possible with signature.\n/// This contract implements those withdrawals for a native underlying asset.\nabstract contract fTokenNativeUnderlyingEIP2612Withdrawals is fTokenNativeUnderlyingActions {\n /// @inheritdoc IFTokenNativeUnderlying\n function withdrawWithSignatureNative(\n uint256 sharesToPermit_,\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_,\n uint256 deadline_,\n bytes calldata signature_\n ) external nonReentrant returns (uint256 shares_) {\n // @dev logic below is exactly the same as in {fTokenEIP2612Withdrawals-withdrawWithSignature}, just using\n // _executeWithdrawNative instead of _executeWithdraw\n\n if (msg.sender == owner_) {\n // no sense in operating with permit if msg.sender is owner. should call normal `withdraw()` instead.\n revert FluidLendingError(ErrorTypes.fToken__PermitFromOwnerCall);\n }\n\n // create allowance through signature_\n _allowViaPermitEIP2612(owner_, sharesToPermit_, deadline_, signature_);\n\n // execute withdraw to get shares_ to spend amount\n shares_ = _executeWithdrawNative(assets_, receiver_, owner_);\n\n _revertIfAboveMaxAmount(shares_, maxSharesBurn_);\n\n _spendAllowance(owner_, msg.sender, shares_);\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function redeemWithSignatureNative(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_,\n uint256 deadline_,\n bytes calldata signature_\n ) external nonReentrant returns (uint256 assets_) {\n // @dev logic below is exactly the same as in {fTokenEIP2612Withdrawals-redeemWithSignature}, just using\n // _executeWithdrawNative instead of _executeWithdraw\n\n if (msg.sender == owner_) {\n // no sense in operating with permit if msg.sender is owner. should call normal `redeem()` instead.\n revert FluidLendingError(ErrorTypes.fToken__PermitFromOwnerCall);\n }\n\n assets_ = previewRedeem(shares_);\n _revertIfBelowMinAmountOut(assets_, minAmountOut_);\n\n // create allowance through signature_\n _allowViaPermitEIP2612(owner_, shares_, deadline_, signature_);\n\n // execute withdraw to get actual shares to spend amount\n uint256 sharesToSpend_ = _executeWithdrawNative(assets_, receiver_, owner_);\n\n _spendAllowance(owner_, msg.sender, sharesToSpend_);\n }\n}\n\n/// @notice Same as the {fToken} contract but with support for native token as underlying asset.\n/// Actual underlying asset is the wrapped native ERC20 version (e.g. WETH), which acts like any other fToken.\n/// But in addition the fTokenNativeUnderlying also has methods for doing all the same actions via the native token.\ncontract fTokenNativeUnderlying is fTokenNativeUnderlyingEIP2612Withdrawals {\n /// @param liquidity_ liquidity contract address\n /// @param lendingFactory_ lending factory contract address\n /// @param weth_ address of wrapped native token (e.g. WETH)\n constructor(\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IWETH9 weth_\n ) fToken(liquidity_, lendingFactory_, IERC20(address(weth_))) {}\n\n /// @inheritdoc fToken\n function liquidityCallback(\n address /** token_ */,\n uint256 /** amount_ */,\n bytes calldata /** data_ */\n ) external virtual override(IFToken, fToken) {\n // not needed because msg.value is used directly\n revert FluidLendingError(ErrorTypes.fTokenNativeUnderlying__UnexpectedLiquidityCallback);\n }\n\n receive() external payable {}\n}\n" }, "contracts/protocols/lending/fToken/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { ERC20Permit } from \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\n\nimport { IAllowanceTransfer } from \"../interfaces/permit2/iAllowanceTransfer.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { IFToken } from \"../interfaces/iFToken.sol\";\nimport { IAllowanceTransfer } from \"../interfaces/permit2/iAllowanceTransfer.sol\";\nimport { IFluidLendingRewardsRateModel } from \"../interfaces/iLendingRewardsRateModel.sol\";\nimport { IFluidLendingFactory } from \"../interfaces/iLendingFactory.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\nabstract contract Constants {\n /// @dev permit2 contract, deployed to same address on EVM networks, see https://github.com/Uniswap/permit2\n IAllowanceTransfer internal constant PERMIT2 = IAllowanceTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3);\n\n /// @dev precision for exchange prices\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n\n /// @dev max allowed reward rate is 50%\n uint256 internal constant MAX_REWARDS_RATE = 50 * 1e12; // 50%;\n\n /// @dev address of the Liquidity contract.\n IFluidLiquidity internal immutable LIQUIDITY;\n\n /// @dev address of the Lending factory contract.\n IFluidLendingFactory internal immutable LENDING_FACTORY;\n\n /// @dev address of the underlying asset contract.\n IERC20 internal immutable ASSET;\n\n /// @dev number of decimals for the fToken, same as ASSET\n uint8 internal immutable DECIMALS;\n\n /// @dev slot ids in Liquidity contract for underlying token.\n /// Helps in low gas fetch from liquidity contract by skipping delegate call with `readFromStorage`\n bytes32 internal immutable LIQUIDITY_EXCHANGE_PRICES_SLOT;\n bytes32 internal immutable LIQUIDITY_TOTAL_AMOUNTS_SLOT;\n bytes32 internal immutable LIQUIDITY_USER_SUPPLY_SLOT;\n\n /// @param liquidity_ liquidity contract address\n /// @param lendingFactory_ lending factory contract address\n /// @param asset_ underlying token address\n constructor(IFluidLiquidity liquidity_, IFluidLendingFactory lendingFactory_, IERC20 asset_) {\n DECIMALS = IERC20Metadata(address(asset_)).decimals();\n ASSET = asset_;\n LIQUIDITY = liquidity_;\n LENDING_FACTORY = lendingFactory_;\n\n LIQUIDITY_EXCHANGE_PRICES_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n _getLiquiditySlotLinksAsset()\n );\n LIQUIDITY_TOTAL_AMOUNTS_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n _getLiquiditySlotLinksAsset()\n );\n LIQUIDITY_USER_SUPPLY_SLOT = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT,\n address(this),\n _getLiquiditySlotLinksAsset()\n );\n }\n\n /// @dev gets asset address for liquidity slot links, extracted to separate method so it can be overridden if needed\n function _getLiquiditySlotLinksAsset() internal view virtual returns (address) {\n return address(ASSET);\n }\n}\n\nabstract contract Variables is ERC20, ERC20Permit, Error, Constants, IFToken {\n /// @dev prefix for token name. fToken will append the underlying asset name\n string private constant TOKEN_NAME_PREFIX = \"Fluid \";\n /// @dev prefix for token symbol. fToken will append the underlying asset symbol\n string private constant TOKEN_SYMBOL_PREFIX = \"f\";\n\n // ------------ storage variables from inherited contracts come before vars here --------\n // _________ ERC20 _______________\n // ----------------------- slot 0 ---------------------------\n // mapping(address => uint256) private _balances;\n\n // ----------------------- slot 1 ---------------------------\n // mapping(address => mapping(address => uint256)) private _allowances;\n\n // ----------------------- slot 2 ---------------------------\n // uint256 private _totalSupply;\n\n // ----------------------- slot 3 ---------------------------\n // string private _name;\n // ----------------------- slot 4 ---------------------------\n // string private _symbol;\n\n // _________ ERC20Permit _______________\n // ----------------------- slot 5 ---------------------------\n // mapping(address => Counters.Counter) private _nonces;\n\n // ----------------------- slot 6 ---------------------------\n // bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;\n\n // ----------------------- slot 7 ---------------------------\n /// @dev address of the LendingRewardsRateModel.\n IFluidLendingRewardsRateModel internal _rewardsRateModel;\n\n // -> 12 bytes empty\n uint96 private __placeholder_gap;\n\n // ----------------------- slot 8 ---------------------------\n // optimized to put all storage variables where a SSTORE happens on actions in the same storage slot\n\n /// @dev exchange price for the underlying assset in the liquidity protocol (without rewards)\n uint64 internal _liquidityExchangePrice; // in 1e12 -> (max value 18_446_744,073709551615)\n\n /// @dev exchange price between fToken and the underlying assset (with rewards)\n uint64 internal _tokenExchangePrice; // in 1e12 -> (max value 18_446_744,073709551615)\n\n /// @dev timestamp when exchange prices were updated the last time\n uint40 internal _lastUpdateTimestamp;\n\n /// @dev status for reentrancy guard\n uint8 internal _status;\n\n /// @dev flag to signal if rewards are active without having to read slot 6\n bool internal _rewardsActive;\n\n // 72 bits empty (9 bytes)\n\n // ----------------------- slot 9 ---------------------------\n /// @dev rebalancer address allowed to call `rebalance()` and source for funding rewards (ReserveContract).\n address internal _rebalancer;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n /// @param liquidity_ liquidity contract address\n /// @param lendingFactory_ lending factory contract address\n /// @param asset_ underlying token address\n constructor(\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IERC20 asset_\n )\n validAddress(address(liquidity_))\n validAddress(address(lendingFactory_))\n validAddress(address(asset_))\n Constants(liquidity_, lendingFactory_, asset_)\n ERC20(\n string(abi.encodePacked(TOKEN_NAME_PREFIX, IERC20Metadata(address(asset_)).name())),\n string(abi.encodePacked(TOKEN_SYMBOL_PREFIX, IERC20Metadata(address(asset_)).symbol()))\n )\n ERC20Permit(string(abi.encodePacked(TOKEN_NAME_PREFIX, IERC20Metadata(address(asset_)).name())))\n {}\n\n /// @dev checks that address is not the zero address, reverts if so. Calling the method in the modifier reduces\n /// bytecode size as modifiers are inlined into bytecode\n function _checkValidAddress(address value_) internal pure {\n if (value_ == address(0)) {\n revert FluidLendingError(ErrorTypes.fToken__InvalidParams);\n }\n }\n\n /// @dev validates that an address is not the zero address\n modifier validAddress(address value_) {\n _checkValidAddress(value_);\n _;\n }\n}\n" }, "contracts/protocols/lending/interfaces/external/iWETH9.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IWETH9 is IERC20 {\n function deposit() external payable;\n\n function withdraw(uint256 wad) external;\n}\n" }, "contracts/protocols/lending/interfaces/iFToken.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\n\nimport { IAllowanceTransfer } from \"./permit2/iAllowanceTransfer.sol\";\nimport { IFluidLendingRewardsRateModel } from \"./iLendingRewardsRateModel.sol\";\nimport { IFluidLendingFactory } from \"./iLendingFactory.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\n\ninterface IFTokenAdmin {\n /// @notice updates the rewards rate model contract.\n /// Only callable by LendingFactory auths.\n /// @param rewardsRateModel_ the new rewards rate model contract address.\n /// can be set to address(0) to set no rewards (to save gas)\n function updateRewards(IFluidLendingRewardsRateModel rewardsRateModel_) external;\n\n /// @notice Balances out the difference between fToken supply at Liquidity vs totalAssets().\n /// Deposits underlying from rebalancer address into Liquidity but doesn't mint any shares\n /// -> thus making deposit available as rewards.\n /// Only callable by rebalancer.\n /// @return assets_ amount deposited to Liquidity\n function rebalance() external payable returns (uint256 assets_);\n\n /// @notice gets the liquidity exchange price of the underlying asset, calculates the updated exchange price (with reward rates)\n /// and writes those values to storage.\n /// Callable by anyone.\n /// @return tokenExchangePrice_ exchange price of fToken share to underlying asset\n /// @return liquidityExchangePrice_ exchange price at Liquidity for the underlying asset\n function updateRates() external returns (uint256 tokenExchangePrice_, uint256 liquidityExchangePrice_);\n\n /// @notice sends any potentially stuck funds to Liquidity contract. Only callable by LendingFactory auths.\n function rescueFunds(address token_) external;\n\n /// @notice Updates the rebalancer address (ReserveContract). Only callable by LendingFactory auths.\n function updateRebalancer(address rebalancer_) external;\n}\n\ninterface IFToken is IERC4626, IFTokenAdmin {\n /// @notice returns minimum amount required for deposit (rounded up)\n function minDeposit() external view returns (uint256);\n\n /// @notice returns config, rewards and exchange prices data in a single view method.\n /// @return liquidity_ address of the Liquidity contract.\n /// @return lendingFactory_ address of the Lending factory contract.\n /// @return lendingRewardsRateModel_ address of the rewards rate model contract. changeable by LendingFactory auths.\n /// @return permit2_ address of the Permit2 contract used for deposits / mint with signature\n /// @return rebalancer_ address of the rebalancer allowed to execute `rebalance()`\n /// @return rewardsActive_ true if rewards are currently active\n /// @return liquidityBalance_ current Liquidity supply balance of `address(this)` for the underyling asset\n /// @return liquidityExchangePrice_ (updated) exchange price for the underlying assset in the liquidity protocol (without rewards)\n /// @return tokenExchangePrice_ (updated) exchange price between fToken and the underlying assset (with rewards)\n function getData()\n external\n view\n returns (\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IFluidLendingRewardsRateModel lendingRewardsRateModel_,\n IAllowanceTransfer permit2_,\n address rebalancer_,\n bool rewardsActive_,\n uint256 liquidityBalance_,\n uint256 liquidityExchangePrice_,\n uint256 tokenExchangePrice_\n );\n\n /// @notice transfers `amount_` of `token_` to liquidity. Only callable by liquidity contract.\n /// @dev this callback is used to optimize gas consumption (reducing necessary token transfers).\n function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external;\n\n /// @notice deposit `assets_` amount with Permit2 signature for underlying asset approval.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached.\n /// `assets_` must at least be `minDeposit()` amount; reverts otherwise.\n /// @param assets_ amount of assets to deposit\n /// @param receiver_ receiver of minted fToken shares\n /// @param minAmountOut_ minimum accepted amount of shares minted\n /// @param permit_ Permit2 permit message\n /// @param signature_ packed signature of signing the EIP712 hash of `permit_`\n /// @return shares_ amount of minted shares\n function depositWithSignature(\n uint256 assets_,\n address receiver_,\n uint256 minAmountOut_,\n IAllowanceTransfer.PermitSingle calldata permit_,\n bytes calldata signature_\n ) external returns (uint256 shares_);\n\n /// @notice mint amount of `shares_` with Permit2 signature for underlying asset approval.\n /// Signature should approve a little bit more than expected assets amount (`previewMint()`) to avoid reverts.\n /// `shares_` must at least be `minMint()` amount; reverts otherwise.\n /// Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.\n /// Recommended to use `deposit()` over mint because it is more gas efficient and less likely to revert.\n /// @param shares_ amount of shares to mint\n /// @param receiver_ receiver of minted fToken shares\n /// @param maxAssets_ maximum accepted amount of assets used as input to mint `shares_`\n /// @param permit_ Permit2 permit message\n /// @param signature_ packed signature of signing the EIP712 hash of `permit_`\n /// @return assets_ deposited assets amount\n function mintWithSignature(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_,\n IAllowanceTransfer.PermitSingle calldata permit_,\n bytes calldata signature_\n ) external returns (uint256 assets_);\n}\n\ninterface IFTokenNativeUnderlying is IFToken {\n /// @notice address that is mapped to the chain native token at Liquidity\n function NATIVE_TOKEN_ADDRESS() external view returns (address);\n\n /// @notice deposits `msg.value` amount of native token for `receiver_`.\n /// `msg.value` must be at least `minDeposit()` amount; reverts otherwise.\n /// Recommended to use `depositNative()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return shares_ actually minted shares\n function depositNative(address receiver_) external payable returns (uint256 shares_);\n\n /// @notice same as {depositNative} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached\n function depositNative(address receiver_, uint256 minAmountOut_) external payable returns (uint256 shares_);\n\n /// @notice mints `shares_` for `receiver_`, paying with underlying native token.\n /// `shares_` must at least be `minMint()` amount; reverts otherwise.\n /// `shares_` set to type(uint256).max not supported.\n /// Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.\n /// Recommended to use `depositNative()` over mint because it is more gas efficient and less likely to revert.\n /// Recommended to use `mintNative()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return assets_ deposited assets amount\n function mintNative(uint256 shares_, address receiver_) external payable returns (uint256 assets_);\n\n /// @notice same as {mintNative} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MaxAmount()` if `maxAssets_` of assets is surpassed to mint `shares_`.\n function mintNative(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_\n ) external payable returns (uint256 assets_);\n\n /// @notice withdraws `assets_` amount in native underlying to `receiver_`, burning shares of `owner_`.\n /// If `assets_` equals uint256.max then the whole fToken balance of `owner_` is withdrawn.This does not\n /// consider withdrawal limit at liquidity so best to check with `maxWithdraw()` before.\n /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.\n /// Recommended to use `withdrawNative()` with a `maxSharesBurn_` param instead to set acceptable limit.\n /// @return shares_ burned shares\n function withdrawNative(uint256 assets_, address receiver_, address owner_) external returns (uint256 shares_);\n\n /// @notice same as {withdrawNative} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MaxAmount()` if `maxSharesBurn_` of shares burned is surpassed.\n function withdrawNative(\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_\n ) external returns (uint256 shares_);\n\n /// @notice redeems `shares_` to native underlying to `receiver_`, burning shares of `owner_`.\n /// If `shares_` equals uint256.max then the whole balance of `owner_` is withdrawn.This does not\n /// consider withdrawal limit at liquidity so best to check with `maxRedeem()` before.\n /// Recommended to use `withdrawNative()` over redeem because it is more gas efficient and can set specific amount.\n /// Recommended to use `redeemNative()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return assets_ withdrawn assets amount\n function redeemNative(uint256 shares_, address receiver_, address owner_) external returns (uint256 assets_);\n\n /// @notice same as {redeemNative} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of assets is not reached.\n function redeemNative(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_\n ) external returns (uint256 assets_);\n\n /// @notice withdraw amount of `assets_` in native token with ERC-2612 permit signature for fToken approval.\n /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.\n /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.\n /// allowance via signature should cover `previewWithdraw(assets_)` plus a little buffer to avoid revert.\n /// Inherent trust assumption that `msg.sender` will set `receiver_` and `minAmountOut_` as `owner_` intends\n /// (which is always the case when giving allowance to some spender).\n /// @param sharesToPermit_ shares amount to use for EIP2612 permit(). Should cover `previewWithdraw(assets_)` + small buffer.\n /// @param assets_ amount of assets to withdraw\n /// @param receiver_ receiver of withdrawn assets\n /// @param owner_ owner to withdraw from (must be signature signer)\n /// @param maxSharesBurn_ maximum accepted amount of shares burned\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for ERC-2612 permit\n /// @return shares_ burned shares amount\n function withdrawWithSignatureNative(\n uint256 sharesToPermit_,\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_,\n uint256 deadline_,\n bytes calldata signature_\n ) external returns (uint256 shares_);\n\n /// @notice redeem amount of `shares_` as native token with ERC-2612 permit signature for fToken approval.\n /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.\n /// Note there might be tiny inaccuracies between requested `shares_` to redeem and actually burned shares.\n /// allowance via signature must cover `shares_` plus a tiny buffer.\n /// Inherent trust assumption that `msg.sender` will set `receiver_` and `minAmountOut_` as `owner_` intends\n /// (which is always the case when giving allowance to some spender).\n /// Recommended to use `withdrawNative()` over redeem because it is more gas efficient and can set specific amount.\n /// @param shares_ amount of shares to redeem\n /// @param receiver_ receiver of withdrawn assets\n /// @param owner_ owner to withdraw from (must be signature signer)\n /// @param minAmountOut_ minimum accepted amount of assets withdrawn\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for ERC-2612 permit\n /// @return assets_ withdrawn assets amount\n function redeemWithSignatureNative(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_,\n uint256 deadline_,\n bytes calldata signature_\n ) external returns (uint256 assets_);\n}\n" }, "contracts/protocols/lending/interfaces/iLendingFactory.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\n\ninterface IFluidLendingFactoryAdmin {\n /// @notice reads if a certain `auth_` address is an allowed auth or not. Owner is auth by default.\n function isAuth(address auth_) external view returns (bool);\n\n /// @notice Sets an address as allowed auth or not. Only callable by owner.\n /// @param auth_ address to set auth value for\n /// @param allowed_ bool flag for whether address is allowed as auth or not\n function setAuth(address auth_, bool allowed_) external;\n\n /// @notice reads if a certain `deployer_` address is an allowed deployer or not. Owner is deployer by default.\n function isDeployer(address deployer_) external view returns (bool);\n\n /// @notice Sets an address as allowed deployer or not. Only callable by owner.\n /// @param deployer_ address to set deployer value for\n /// @param allowed_ bool flag for whether address is allowed as deployer or not\n function setDeployer(address deployer_, bool allowed_) external;\n\n /// @notice Sets the `creationCode_` bytecode for a certain `fTokenType_`. Only callable by auths.\n /// @param fTokenType_ the fToken Type used to refer the creation code\n /// @param creationCode_ contract creation code. can be set to bytes(0) to remove a previously available `fTokenType_`\n function setFTokenCreationCode(string memory fTokenType_, bytes calldata creationCode_) external;\n\n /// @notice creates token for `asset_` for a lending protocol with interest. Only callable by deployers.\n /// @param asset_ address of the asset\n /// @param fTokenType_ type of fToken:\n /// - if it's the native token, it should use `NativeUnderlying`\n /// - otherwise it should use `fToken`\n /// - could be more types available, check `fTokenTypes()`\n /// @param isNativeUnderlying_ flag to signal fToken type that uses native underlying at Liquidity\n /// @return token_ address of the created token\n function createToken(\n address asset_,\n string calldata fTokenType_,\n bool isNativeUnderlying_\n ) external returns (address token_);\n}\n\ninterface IFluidLendingFactory is IFluidLendingFactoryAdmin {\n /// @notice list of all created tokens\n function allTokens() external view returns (address[] memory);\n\n /// @notice list of all fToken types that can be deployed\n function fTokenTypes() external view returns (string[] memory);\n\n /// @notice returns the creation code for a certain `fTokenType_`\n function fTokenCreationCode(string memory fTokenType_) external view returns (bytes memory);\n\n /// @notice address of the Liquidity contract.\n function LIQUIDITY() external view returns (IFluidLiquidity);\n\n /// @notice computes deterministic token address for `asset_` for a lending protocol\n /// @param asset_ address of the asset\n /// @param fTokenType_ type of fToken:\n /// - if it's the native token, it should use `NativeUnderlying`\n /// - otherwise it should use `fToken`\n /// - could be more types available, check `fTokenTypes()`\n /// @return token_ detemrinistic address of the computed token\n function computeToken(address asset_, string calldata fTokenType_) external view returns (address token_);\n}\n" }, "contracts/protocols/lending/interfaces/iLendingRewardsRateModel.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidLendingRewardsRateModel {\n /// @notice Calculates the current rewards rate (APR)\n /// @param totalAssets_ amount of assets in the lending\n /// @return rate_ rewards rate percentage per year with 1e12 RATE_PRECISION, e.g. 1e12 = 1%, 1e14 = 100%\n /// @return ended_ flag to signal that rewards have ended (always 0 going forward)\n /// @return startTime_ start time of rewards to compare against last update timestamp\n function getRate(uint256 totalAssets_) external view returns (uint256 rate_, bool ended_, uint256 startTime_);\n\n /// @notice Returns config constants for rewards rate model\n function getConfig()\n external\n view\n returns (\n uint256 duration_,\n uint256 startTime_,\n uint256 endTime_,\n uint256 startTvl_,\n uint256 maxRate_,\n uint256 rewardAmount_,\n address initiator_\n );\n}\n" }, "contracts/protocols/lending/interfaces/iStakingRewards.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IFluidLendingStakingRewards {\n // Views\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function earned(address account) external view returns (uint256);\n\n function getRewardForDuration() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function periodFinish() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardsDuration() external view returns (uint256);\n\n function rewardsToken() external view returns (IERC20);\n\n function stakingToken() external view returns (IERC20);\n\n // Mutative\n\n function stake(uint256 amount) external;\n\n function withdraw(uint256 amount) external;\n\n function getReward() external;\n\n function exit() external;\n}\n" }, "contracts/protocols/lending/interfaces/permit2/iAllowanceTransfer.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\n/// @title AllowanceTransfer\n/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts\n/// @dev Requires user's token approval on the Permit2 contract\n/// from https://github.com/Uniswap/permit2/blob/main/src/interfaces/ISignatureTransfer.sol.\n/// Copyright (c) 2022 Uniswap Labs\ninterface IAllowanceTransfer {\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n /// @notice Thrown when an allowance on a token has expired.\n /// @param deadline The timestamp at which the allowed amount is no longer valid\n error AllowanceExpired(uint256 deadline);\n\n /// @notice Thrown when an allowance on a token has been depleted.\n /// @param amount The maximum amount allowed\n error InsufficientAllowance(uint256 amount);\n\n /// @notice Thrown when too many nonces are invalidated.\n error ExcessiveInvalidation();\n\n /// @notice Emits an event when the owner successfully invalidates an ordered nonce.\n event NonceInvalidation(\n address indexed owner,\n address indexed token,\n address indexed spender,\n uint48 newNonce,\n uint48 oldNonce\n );\n\n /// @notice Emits an event when the owner successfully sets permissions on a token for the spender.\n event Approval(\n address indexed owner,\n address indexed token,\n address indexed spender,\n uint160 amount,\n uint48 expiration\n );\n\n /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.\n event Permit(\n address indexed owner,\n address indexed token,\n address indexed spender,\n uint160 amount,\n uint48 expiration,\n uint48 nonce\n );\n\n /// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.\n event Lockdown(address indexed owner, address token, address spender);\n\n /// @notice The permit data for a token\n struct PermitDetails {\n // ERC20 token address\n address token;\n // the maximum amount allowed to spend\n uint160 amount;\n // timestamp at which a spender's token allowances become invalid\n uint48 expiration;\n // an incrementing value indexed per owner,token,and spender for each signature\n uint48 nonce;\n }\n\n /// @notice The permit message signed for a single token allownce\n struct PermitSingle {\n // the permit data for a single token alownce\n PermitDetails details;\n // address permissioned on the allowed tokens\n address spender;\n // deadline on the permit signature\n uint256 sigDeadline;\n }\n\n /// @notice The permit message signed for multiple token allowances\n struct PermitBatch {\n // the permit data for multiple token allowances\n PermitDetails[] details;\n // address permissioned on the allowed tokens\n address spender;\n // deadline on the permit signature\n uint256 sigDeadline;\n }\n\n /// @notice The saved permissions\n /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message\n /// @dev Setting amount to type(uint160).max sets an unlimited approval\n struct PackedAllowance {\n // amount allowed\n uint160 amount;\n // permission expiry\n uint48 expiration;\n // an incrementing value indexed per owner,token,and spender for each signature\n uint48 nonce;\n }\n\n /// @notice A token spender pair.\n struct TokenSpenderPair {\n // the token the spender is approved\n address token;\n // the spender address\n address spender;\n }\n\n /// @notice Details for a token transfer.\n struct AllowanceTransferDetails {\n // the owner of the token\n address from;\n // the recipient of the token\n address to;\n // the amount of the token\n uint160 amount;\n // the token to be transferred\n address token;\n }\n\n /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.\n /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]\n /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.\n function allowance(\n address user,\n address token,\n address spender\n ) external view returns (uint160 amount, uint48 expiration, uint48 nonce);\n\n /// @notice Approves the spender to use up to amount of the specified token up until the expiration\n /// @param token The token to approve\n /// @param spender The spender address to approve\n /// @param amount The approved amount of the token\n /// @param expiration The timestamp at which the approval is no longer valid\n /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve\n /// @dev Setting amount to type(uint160).max sets an unlimited approval\n function approve(address token, address spender, uint160 amount, uint48 expiration) external;\n\n /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature\n /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce\n /// @param owner The owner of the tokens being approved\n /// @param permitSingle Data signed over by the owner specifying the terms of approval\n /// @param signature The owner's signature over the permit data\n function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;\n\n /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature\n /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce\n /// @param owner The owner of the tokens being approved\n /// @param permitBatch Data signed over by the owner specifying the terms of approval\n /// @param signature The owner's signature over the permit data\n function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;\n\n /// @notice Transfer approved tokens from one address to another\n /// @param from The address to transfer from\n /// @param to The address of the recipient\n /// @param amount The amount of the token to transfer\n /// @param token The token address to transfer\n /// @dev Requires the from address to have approved at least the desired amount\n /// of tokens to msg.sender.\n function transferFrom(address from, address to, uint160 amount, address token) external;\n\n /// @notice Transfer approved tokens in a batch\n /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers\n /// @dev Requires the from addresses to have approved at least the desired amount\n /// of tokens to msg.sender.\n function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;\n\n /// @notice Enables performing a \"lockdown\" of the sender's Permit2 identity\n /// by batch revoking approvals\n /// @param approvals Array of approvals to revoke.\n function lockdown(TokenSpenderPair[] calldata approvals) external;\n\n /// @notice Invalidate nonces for a given (token, spender) pair\n /// @param token The token to invalidate nonces for\n /// @param spender The spender to invalidate nonces for\n /// @param newNonce The new nonce to set. Invalidates all nonces less than it.\n /// @dev Can't invalidate more than 2**16 nonces per transaction.\n function invalidateNonces(address token, address spender, uint48 newNonce) external;\n}\n" }, "contracts/protocols/lending/interfaces/permit2/IEIP712.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IEIP712 {\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" }, "contracts/protocols/lending/interfaces/permit2/IERC2612.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n// ERC20 with EIP2612 extension functions.\ninterface IERC2612 is IERC20 {\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function nonces(address owner) external view returns (uint);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" }, "contracts/protocols/lending/interfaces/permit2/ISignatureTransfer.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IEIP712 } from \"./IEIP712.sol\";\n\n/// @title SignatureTransfer\n/// @notice Handles ERC20 token transfers through signature based actions\n/// @dev Requires user's token approval on the Permit2 contract\ninterface ISignatureTransfer is IEIP712 {\n /// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount\n /// @param maxAmount The maximum amount a spender can request to transfer\n error InvalidAmount(uint256 maxAmount);\n\n /// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred\n /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred\n error LengthMismatch();\n\n /// @notice Emits an event when the owner successfully invalidates an unordered nonce.\n event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);\n\n /// @notice The token and amount details for a transfer signed in the permit transfer signature\n struct TokenPermissions {\n // ERC20 token address\n address token;\n // the maximum amount that can be spent\n uint256 amount;\n }\n\n /// @notice The signed permit message for a single token transfer\n struct PermitTransferFrom {\n TokenPermissions permitted;\n // a unique value for every token owner's signature to prevent signature replays\n uint256 nonce;\n // deadline on the permit signature\n uint256 deadline;\n }\n\n /// @notice Specifies the recipient address and amount for batched transfers.\n /// @dev Recipients and amounts correspond to the index of the signed token permissions array.\n /// @dev Reverts if the requested amount is greater than the permitted signed amount.\n struct SignatureTransferDetails {\n // recipient address\n address to;\n // spender requested amount\n uint256 requestedAmount;\n }\n\n /// @notice Used to reconstruct the signed permit message for multiple token transfers\n /// @dev Do not need to pass in spender address as it is required that it is msg.sender\n /// @dev Note that a user still signs over a spender address\n struct PermitBatchTransferFrom {\n // the tokens and corresponding amounts permitted for a transfer\n TokenPermissions[] permitted;\n // a unique value for every token owner's signature to prevent signature replays\n uint256 nonce;\n // deadline on the permit signature\n uint256 deadline;\n }\n\n /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection\n /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order\n /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce\n /// @dev It returns a uint256 bitmap\n /// @dev The index, or wordPosition is capped at type(uint248).max\n function nonceBitmap(address, uint256) external view returns (uint256);\n\n /// @notice Transfers a token using a signed permit message\n /// @dev Reverts if the requested amount is greater than the permitted signed amount\n /// @param permit The permit data signed over by the owner\n /// @param owner The owner of the tokens to transfer\n /// @param transferDetails The spender's requested transfer details for the permitted token\n /// @param signature The signature to verify\n function permitTransferFrom(\n PermitTransferFrom memory permit,\n SignatureTransferDetails calldata transferDetails,\n address owner,\n bytes calldata signature\n ) external;\n\n /// @notice Transfers a token using a signed permit message\n /// @notice Includes extra data provided by the caller to verify signature over\n /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition\n /// @dev Reverts if the requested amount is greater than the permitted signed amount\n /// @param permit The permit data signed over by the owner\n /// @param owner The owner of the tokens to transfer\n /// @param transferDetails The spender's requested transfer details for the permitted token\n /// @param witness Extra data to include when checking the user signature\n /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash\n /// @param signature The signature to verify\n function permitWitnessTransferFrom(\n PermitTransferFrom memory permit,\n SignatureTransferDetails calldata transferDetails,\n address owner,\n bytes32 witness,\n string calldata witnessTypeString,\n bytes calldata signature\n ) external;\n\n /// @notice Transfers multiple tokens using a signed permit message\n /// @param permit The permit data signed over by the owner\n /// @param owner The owner of the tokens to transfer\n /// @param transferDetails Specifies the recipient and requested amount for the token transfer\n /// @param signature The signature to verify\n function permitTransferFrom(\n PermitBatchTransferFrom memory permit,\n SignatureTransferDetails[] calldata transferDetails,\n address owner,\n bytes calldata signature\n ) external;\n\n /// @notice Transfers multiple tokens using a signed permit message\n /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition\n /// @notice Includes extra data provided by the caller to verify signature over\n /// @param permit The permit data signed over by the owner\n /// @param owner The owner of the tokens to transfer\n /// @param transferDetails Specifies the recipient and requested amount for the token transfer\n /// @param witness Extra data to include when checking the user signature\n /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash\n /// @param signature The signature to verify\n function permitWitnessTransferFrom(\n PermitBatchTransferFrom memory permit,\n SignatureTransferDetails[] calldata transferDetails,\n address owner,\n bytes32 witness,\n string calldata witnessTypeString,\n bytes calldata signature\n ) external;\n\n /// @notice Invalidates the bits specified in mask for the bitmap at the word position\n /// @dev The wordPos is maxed at type(uint248).max\n /// @param wordPos A number to index the nonceBitmap at\n /// @param mask A bitmap masked against msg.sender's current bitmap at the word position\n function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;\n}\n" }, "contracts/protocols/lending/lendingFactory/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLendingFactory } from \"../interfaces/iLendingFactory.sol\";\n\nabstract contract Events {\n /// @notice emitted when a new fToken is created\n event LogTokenCreated(address indexed token, address indexed asset, uint256 indexed count, string fTokenType);\n\n /// @notice emitted when an auth is modified by owner\n event LogSetAuth(address indexed auth, bool indexed allowed);\n\n /// @notice emitted when a deployer is modified by owner\n event LogSetDeployer(address indexed deployer, bool indexed allowed);\n\n /// @notice emitted when the creation code for an fTokenType is set\n event LogSetFTokenCreationCode(string indexed fTokenType, address indexed creationCodePointer);\n}\n" }, "contracts/protocols/lending/lendingFactory/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { CREATE3 } from \"solmate/src/utils/CREATE3.sol\";\nimport { SSTORE2 } from \"solmate/src/utils/SSTORE2.sol\";\nimport { Owned } from \"solmate/src/auth/Owned.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { IFluidLendingFactory, IFluidLendingFactoryAdmin } from \"../interfaces/iLendingFactory.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\nimport { Events } from \"./events.sol\";\n\nabstract contract LendingFactoryVariables is Owned, Error, IFluidLendingFactory {\n /*//////////////////////////////////////////////////////////////\n CONSTANTS / IMMUTABLES\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFluidLendingFactory\n IFluidLiquidity public immutable LIQUIDITY;\n\n /// @dev address that is mapped to the chain native token\n address internal constant _NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /*//////////////////////////////////////////////////////////////\n STORAGE VARIABLES\n //////////////////////////////////////////////////////////////*/\n\n // ------------ storage variables from inherited contracts (Owned) come before vars here --------\n\n // ----------------------- slot 0 ---------------------------\n // address public owner;\n\n // 12 bytes empty\n\n // ----------------------- slot 1 ---------------------------\n /// @dev auths can update rewards related config at created fToken contracts.\n /// owner can add/remove auths.\n /// Owner is auth by default.\n mapping(address => uint256) internal _auths;\n\n // ----------------------- slot 2 ---------------------------\n /// @dev deployers can deploy new fTokens.\n /// owner can add/remove deployers.\n /// Owner is deployer by default.\n mapping(address => uint256) internal _deployers;\n\n // ----------------------- slot 3 ---------------------------\n /// @dev list of all created tokens.\n /// Solidity creates an automatic getter only to fetch at a certain position, so explicitly define a getter that returns all.\n address[] internal _allTokens;\n\n // ----------------------- slot 4 ---------------------------\n\n /// @dev available fTokenTypes for deployment. At least EIP2612Deposits, Permit2Deposits, NativeUnderlying.\n /// Solidity creates an automatic getter only to fetch at a certain position, so explicitly define a getter that returns all.\n string[] internal _fTokenTypes;\n\n // ----------------------- slot 5 ---------------------------\n\n /// @dev fToken creation code for each fTokenType, accessed via SSTORE2.\n /// maps keccak256(abi.encode(fTokenType)) -> SSTORE2 written creation code for the fToken contract\n mapping(bytes32 => address) internal _fTokenCreationCodePointers;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(IFluidLiquidity liquidity_, address owner_) Owned(owner_) {\n if (owner_ == address(0)) {\n // Owned does not have a zero check for owner_\n revert FluidLendingError(ErrorTypes.LendingFactory__ZeroAddress);\n }\n\n LIQUIDITY = liquidity_;\n }\n\n /// @inheritdoc IFluidLendingFactory\n function allTokens() public view returns (address[] memory) {\n return _allTokens;\n }\n\n /// @inheritdoc IFluidLendingFactory\n function fTokenTypes() public view returns (string[] memory) {\n return _fTokenTypes;\n }\n\n /// @inheritdoc IFluidLendingFactory\n function fTokenCreationCode(string memory fTokenType_) public view returns (bytes memory) {\n address creationCodePointer_ = _fTokenCreationCodePointers[keccak256(abi.encode(fTokenType_))];\n return creationCodePointer_ == address(0) ? new bytes(0) : SSTORE2.read(creationCodePointer_);\n }\n}\n\nabstract contract LendingFactoryAdmin is LendingFactoryVariables, Events {\n /// @dev validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidLendingError(ErrorTypes.LendingFactory__ZeroAddress);\n }\n _;\n }\n\n /// @dev validates that msg.sender is auth or owner\n modifier onlyAuths() {\n if (!isAuth(msg.sender)) {\n revert FluidLendingError(ErrorTypes.LendingFactory__Unauthorized);\n }\n _;\n }\n\n /// @dev validates that msg.sender is deployer or owner\n modifier onlyDeployers() {\n if (!isDeployer(msg.sender)) {\n revert FluidLendingError(ErrorTypes.LendingFactory__Unauthorized);\n }\n _;\n }\n\n /// @inheritdoc IFluidLendingFactoryAdmin\n function isAuth(address auth_) public view returns (bool) {\n return auth_ == owner || _auths[auth_] == 1;\n }\n\n /// @inheritdoc IFluidLendingFactoryAdmin\n function isDeployer(address deployer_) public view returns (bool) {\n return deployer_ == owner || _deployers[deployer_] == 1;\n }\n\n /// @inheritdoc IFluidLendingFactoryAdmin\n function setAuth(address auth_, bool allowed_) external onlyOwner validAddress(auth_) {\n _auths[auth_] = allowed_ ? 1 : 0;\n\n emit LogSetAuth(auth_, allowed_);\n }\n\n /// @inheritdoc IFluidLendingFactoryAdmin\n function setDeployer(address deployer_, bool allowed_) external onlyOwner validAddress(deployer_) {\n _deployers[deployer_] = allowed_ ? 1 : 0;\n\n emit LogSetDeployer(deployer_, allowed_);\n }\n\n /// @inheritdoc IFluidLendingFactoryAdmin\n function setFTokenCreationCode(string memory fTokenType_, bytes calldata creationCode_) external onlyAuths {\n uint256 length_ = _fTokenTypes.length;\n bytes32 fTokenTypeHash_ = keccak256(abi.encode(fTokenType_));\n\n if (creationCode_.length == 0) {\n // remove any previously stored creation code for `fTokenType_`\n delete _fTokenCreationCodePointers[keccak256(abi.encode(fTokenType_))];\n\n // remove key from array _fTokenTypes. _fTokenTypes is most likely an array of very few elements,\n // where setFTokenCreationCode is a rarely called method and the removal of an fTokenType is even more rare.\n // So gas cost is not really an issue here but even if it were, this should still be cheaper than having\n // an additional mapping like with an OpenZeppelin EnumerableSet\n for (uint256 i; i < length_; ++i) {\n if (keccak256(abi.encode(_fTokenTypes[i])) == fTokenTypeHash_) {\n _fTokenTypes[i] = _fTokenTypes[length_ - 1];\n _fTokenTypes.pop();\n break;\n }\n }\n\n emit LogSetFTokenCreationCode(fTokenType_, address(0));\n } else {\n // write creation code to SSTORE2 pointer and set in mapping\n address creationCodePointer_ = SSTORE2.write(creationCode_);\n _fTokenCreationCodePointers[keccak256(abi.encode(fTokenType_))] = creationCodePointer_;\n\n // make sure `fTokenType_` is present in array _fTokenTypes\n bool isPresent_;\n for (uint256 i; i < length_; ++i) {\n if (keccak256(abi.encode(_fTokenTypes[i])) == fTokenTypeHash_) {\n isPresent_ = true;\n break;\n }\n }\n if (!isPresent_) {\n _fTokenTypes.push(fTokenType_);\n }\n\n emit LogSetFTokenCreationCode(fTokenType_, creationCodePointer_);\n }\n }\n\n /// @inheritdoc IFluidLendingFactoryAdmin\n function createToken(\n address asset_,\n string calldata fTokenType_,\n bool isNativeUnderlying_\n ) external validAddress(asset_) onlyDeployers returns (address token_) {\n address creationCodePointer_ = _fTokenCreationCodePointers[keccak256(abi.encode(fTokenType_))];\n if (creationCodePointer_ == address(0)) {\n revert FluidLendingError(ErrorTypes.LendingFactory__InvalidParams);\n }\n\n bytes32 salt_ = _getSalt(asset_, fTokenType_);\n\n if (Address.isContract(CREATE3.getDeployed(salt_))) {\n // revert if token already exists (Solmate CREATE3 does not check before deploying)\n revert FluidLendingError(ErrorTypes.LendingFactory__TokenExists);\n }\n\n bytes32 liquidityExchangePricesSlot_ = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n // native underlying always uses the native token at Liquidity, but also supports WETH\n isNativeUnderlying_ ? _NATIVE_TOKEN_ADDRESS : asset_\n );\n if (LIQUIDITY.readFromStorage(liquidityExchangePricesSlot_) == 0) {\n // revert if fToken has not been configured at Liquidity contract yet (exchange prices config)\n revert FluidLendingError(ErrorTypes.LendingFactory__LiquidityNotConfigured);\n }\n\n // Use CREATE3 for deterministic deployments. Unfortunately it has 55k gas overhead\n token_ = CREATE3.deploy(\n salt_,\n abi.encodePacked(\n SSTORE2.read(creationCodePointer_), // creation code\n abi.encode(LIQUIDITY, address(this), asset_) // constructor params\n ),\n 0\n );\n\n // Add the created token to the allTokens array\n _allTokens.push(token_);\n\n // Emit the TokenCreated event\n emit LogTokenCreated(token_, asset_, _allTokens.length, fTokenType_);\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL\n //////////////////////////////////////////////////////////////*/\n\n /// @dev gets the CREATE3 salt for `asset_` and `fTokenType_`\n function _getSalt(address asset_, string calldata fTokenType_) internal pure returns (bytes32) {\n return keccak256(abi.encode(asset_, fTokenType_));\n }\n}\n\n/// @title Fluid LendingFactory\n/// @notice creates Fluid lending protocol fTokens, which are interacting with Fluid Liquidity.\n/// fTokens are ERC20 & ERC4626 compatible tokens that allow to deposit to Fluid Liquidity to earn interest.\n/// Tokens are created at a deterministic address (see `computeToken()`), only executable by allow-listed auths.\n/// @dev Note the deployed token starts out with no config at Liquidity contract.\n/// This must be done by Liquidity auths in a separate step, otherwise no deposits will be possible.\n/// This contract is not upgradeable. It supports adding new fToken creation codes for future new fToken types.\ncontract FluidLendingFactory is LendingFactoryVariables, LendingFactoryAdmin {\n /// @notice initialize liquidity contract address & owner\n constructor(\n IFluidLiquidity liquidity_,\n address owner_\n ) validAddress(address(liquidity_)) validAddress(owner) LendingFactoryVariables(liquidity_, owner_) {}\n\n /// @inheritdoc IFluidLendingFactory\n function computeToken(address asset_, string calldata fTokenType_) public view returns (address token_) {\n return CREATE3.getDeployed(_getSalt(asset_, fTokenType_));\n }\n}\n" }, "contracts/protocols/lending/lendingRewardsRateModel/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLendingRewardsRateModel } from \"../interfaces/iLendingRewardsRateModel.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\n/// @title LendingRewardsRateModel\n/// @notice Calculates rewards rate used for an fToken based on a rewardAmount over a given duration. \n/// Rewards start once the allowed initiator address triggers `start()` and only accrue above a certain startTVL.\n/// Max rate cap is at 50%.\ncontract FluidLendingRewardsRateModel is IFluidLendingRewardsRateModel, Error {\n /// @notice Emitted when rewards are started\n event LogRewardsStarted(uint256 startTime, uint256 endTime);\n\n /// @dev precision decimals for rewards rate\n uint256 internal constant RATE_PRECISION = 1e12;\n\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n\n /// @dev maximum rewards rate is 50%. no config higher than this should be possible.\n uint256 internal constant MAX_RATE = 50 * RATE_PRECISION; // 1e12 = 1%, this is 50%.\n\n /// @dev tvl below which rewards rate is 0\n uint256 internal immutable START_TVL;\n\n /// @dev for how long rewards should run\n uint256 internal immutable DURATION;\n\n /// @dev annualized reward based on constructor input params (duration, rewardAmount)\n uint256 internal immutable YEARLY_REWARD;\n\n /// @dev total amounts to be distributed. not needed but stored for easier tracking via `getConfig`\n uint256 internal immutable REWARD_AMOUNT;\n\n /// @dev address which has access to call start() which kickstarts the rewards\n address internal immutable INITIATOR;\n\n /// @dev when rewards got started\n uint96 internal startTime;\n /// @dev when rewards will get over\n uint96 internal endTime;\n\n /// @dev Validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__ZeroAddress);\n }\n _;\n }\n\n /// @notice sets immutable vars for rewards rate config based on input params.\n /// @param duration_ for how long rewards should run\n /// @param startTvl_ tvl below which rate is 0\n /// @param rewardAmount_ total amount of underlying asset to be distributed as rewards\n /// @param initiator_ address which has access to kickstart the rewards\n constructor(uint256 duration_, uint256 startTvl_, uint256 rewardAmount_, address initiator_) validAddress(initiator_) {\n // sanity checks\n if (duration_ == 0 || rewardAmount_ == 0 || startTvl_ == 0) {\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__InvalidParams);\n }\n\n START_TVL = startTvl_;\n DURATION = duration_;\n REWARD_AMOUNT = rewardAmount_;\n INITIATOR = initiator_;\n\n YEARLY_REWARD = (rewardAmount_ * SECONDS_PER_YEAR) / DURATION;\n }\n\n /// @inheritdoc IFluidLendingRewardsRateModel\n function getConfig()\n external\n view\n returns (\n uint256 duration_,\n uint256 startTime_,\n uint256 endTime_,\n uint256 startTvl_,\n uint256 maxRate_,\n uint256 rewardAmount_,\n address initiator_\n )\n {\n return (DURATION, startTime, endTime, START_TVL, MAX_RATE, REWARD_AMOUNT, INITIATOR);\n }\n\n /// @inheritdoc IFluidLendingRewardsRateModel\n function getRate(uint256 totalAssets_) public view returns (uint256 rate_, bool ended_, uint256 startTime_) {\n startTime_ = startTime;\n uint endTime_ = endTime;\n if (startTime_ == 0 || endTime_ == 0) {\n return (0, false, startTime_);\n }\n if (block.timestamp > endTime_) {\n return (0, true, startTime_);\n }\n if (totalAssets_ < START_TVL) {\n return (0, false, startTime_);\n }\n\n rate_ = (YEARLY_REWARD * 1e14) / totalAssets_;\n\n return (rate_ > MAX_RATE ? MAX_RATE : rate_, false, startTime_);\n }\n\n function start() external {\n if (msg.sender != INITIATOR) {\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__NotTheInitiator);\n }\n if (startTime > 0 || endTime > 0) {\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__AlreadyStarted);\n }\n startTime = uint96(block.timestamp);\n endTime = uint96(block.timestamp + DURATION);\n\n emit LogRewardsStarted(startTime, endTime);\n }\n\n}\n" }, "contracts/protocols/lending/merkleDistributor/errors.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Errors {\n error Unauthorized();\n error InvalidParams();\n\n // claim related errors:\n error InvalidCycle();\n error InvalidProof();\n error NothingToClaim();\n}\n" }, "contracts/protocols/lending/merkleDistributor/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Events {\n /// @notice Emitted when an address is added or removed from the allowed proposers\n event LogUpdateProposer(address proposer, bool isProposer);\n\n /// @notice Emitted when a new cycle root hash is proposed\n event LogRootProposed(uint256 cycle, bytes32 root, bytes32 contentHash, uint256 timestamp, uint256 blockNumber);\n\n /// @notice Emitted when a new cycle root hash is approved by the owner and becomes the new active root\n event LogRootUpdated(uint256 cycle, bytes32 root, bytes32 contentHash, uint256 timestamp, uint256 blockNumber);\n\n /// @notice Emitted when a `user` claims `amount` via a valid merkle proof\n event LogClaimed(\n address user,\n uint256 amount,\n uint256 cycle,\n address fToken,\n uint256 timestamp,\n uint256 blockNumber\n );\n}\n" }, "contracts/protocols/lending/merkleDistributor/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MerkleProof } from \"@openzeppelin/contracts/utils/cryptography/MerkleProof.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Structs } from \"./structs.sol\";\nimport { Variables } from \"./variables.sol\";\nimport { Events } from \"./events.sol\";\nimport { Errors } from \"./errors.sol\";\n\n// ---------------------------------------------------------------------------------------------\n//\n// @dev WARNING: DO NOT USE `multiProof` related methods of `MerkleProof`.\n// This repo uses OpenZeppelin 4.8.2 which has a vulnerability for multi proofs. See:\n// https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-wprv-93r4-jj2p\n//\n// ---------------------------------------------------------------------------------------------\n\nabstract contract FluidMerkleDistributorCore is Structs, Variables, Events, Errors {\n /// @dev validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert InvalidParams();\n }\n _;\n }\n}\n\nabstract contract FluidMerkleDistributorAdmin is FluidMerkleDistributorCore {\n /// @notice Updates an address status as a root proposer\n /// @param proposer_ The address to update\n /// @param isProposer_ Whether or not the address should be an allowed proposer\n function updateProposer(address proposer_, bool isProposer_) public onlyOwner validAddress(proposer_) {\n _proposers[proposer_] = isProposer_;\n emit LogUpdateProposer(proposer_, isProposer_);\n }\n\n /// @notice Approve the current pending root and content hash\n function approveRoot(\n bytes32 root_,\n bytes32 contentHash_,\n uint40 cycle_,\n uint40 startBlock_,\n uint40 endBlock_\n ) external onlyOwner {\n MerkleCycle memory merkleCycle_ = _pendingMerkleCycle;\n\n if (\n root_ != merkleCycle_.merkleRoot ||\n contentHash_ != merkleCycle_.merkleContentHash ||\n cycle_ != merkleCycle_.cycle ||\n startBlock_ != merkleCycle_.startBlock ||\n endBlock_ != merkleCycle_.endBlock\n ) {\n revert InvalidParams();\n }\n\n previousMerkleRoot = _currentMerkleCycle.merkleRoot;\n\n merkleCycle_.timestamp = uint40(block.timestamp);\n merkleCycle_.publishBlock = uint40(block.number);\n\n _currentMerkleCycle = merkleCycle_;\n\n emit LogRootUpdated(cycle_, root_, contentHash_, block.timestamp, block.number);\n }\n\n /// @dev open payload method for admin to resolve emergency cases\n function spell(address[] memory targets_, bytes[] memory calldatas_) public onlyOwner {\n for (uint256 i = 0; i < targets_.length; i++) {\n Address.functionDelegateCall(targets_[i], calldatas_[i]);\n }\n }\n\n /// @notice Pause contract functionality of new roots and claiming\n function pause() external onlyOwner {\n _pause();\n }\n\n /// @notice Unpause contract functionality of new roots and claiming\n function unpause() external onlyOwner {\n _unpause();\n }\n}\n\nabstract contract FluidMerkleDistributorProposer is FluidMerkleDistributorCore {\n /// @dev Checks that the sender is a proposer\n modifier onlyProposer() {\n if (!isProposer(msg.sender)) {\n revert Unauthorized();\n }\n _;\n }\n\n /// @notice checks if the `proposer_` is an allowed root proposer\n function isProposer(address proposer_) public view returns (bool) {\n return (_proposers[proposer_] || owner == proposer_);\n }\n\n /// @notice Propose a new root and content hash, which will be stored as pending until approved\n function proposeRoot(\n bytes32 root_,\n bytes32 contentHash_,\n uint40 cycle_,\n uint40 startBlock_,\n uint40 endBlock_\n ) external whenNotPaused onlyProposer {\n if (cycle_ != _currentMerkleCycle.cycle + 1 || startBlock_ > endBlock_) {\n revert InvalidParams();\n }\n\n _pendingMerkleCycle = MerkleCycle({\n merkleRoot: root_,\n merkleContentHash: contentHash_,\n cycle: cycle_,\n startBlock: startBlock_,\n endBlock: endBlock_,\n timestamp: uint40(block.timestamp),\n publishBlock: uint40(block.number)\n });\n\n emit LogRootProposed(cycle_, root_, contentHash_, block.timestamp, block.number);\n }\n}\n\ncontract FluidMerkleDistributor is\n FluidMerkleDistributorCore,\n FluidMerkleDistributorAdmin,\n FluidMerkleDistributorProposer\n{\n constructor(address owner_, address proposer_) validAddress(owner_) validAddress(proposer_) Variables(owner_) {\n _proposers[proposer_] = true;\n emit LogUpdateProposer(proposer_, true);\n }\n\n /// @notice checks if there is a proposed root waiting to be approved\n function hasPendingRoot() external view returns (bool) {\n return _pendingMerkleCycle.cycle == _currentMerkleCycle.cycle + 1;\n }\n\n /// @notice merkle root data related to current cycle (proposed and approved).\n function currentMerkleCycle() public view returns (MerkleCycle memory) {\n return _currentMerkleCycle;\n }\n\n /// @notice merkle root data related to pending cycle (proposed but not yet approved).\n function pendingMerkleCycle() public view returns (MerkleCycle memory) {\n return _pendingMerkleCycle;\n }\n\n function encodeClaim(\n address recipient_,\n uint256 cumulativeAmount_,\n address fToken_,\n uint256 cycle_\n ) public pure returns (bytes memory encoded_, bytes32 hash_) {\n encoded_ = abi.encode(fToken_, recipient_, cycle_, cumulativeAmount_);\n hash_ = keccak256(bytes.concat(keccak256(encoded_)));\n }\n\n function claim(\n address recipient_,\n uint256 cumulativeAmount_,\n address fToken_,\n uint256 cycle_,\n bytes32[] calldata merkleProof_\n ) external whenNotPaused {\n uint256 currentCycle_ = uint256(_currentMerkleCycle.cycle);\n\n if (!(cycle_ == currentCycle_ || (currentCycle_ > 0 && cycle_ == currentCycle_ - 1))) {\n revert InvalidCycle();\n }\n\n // Verify the merkle proof.\n bytes32 node_ = keccak256(bytes.concat(keccak256(abi.encode(fToken_, recipient_, cycle_, cumulativeAmount_))));\n if (\n !MerkleProof.verify(\n merkleProof_,\n cycle_ == currentCycle_ ? _currentMerkleCycle.merkleRoot : previousMerkleRoot,\n node_\n )\n ) {\n revert InvalidProof();\n }\n\n uint256 claimable_ = cumulativeAmount_ - claimed[recipient_][fToken_];\n if (claimable_ == 0) {\n revert NothingToClaim();\n }\n\n claimed[recipient_][fToken_] = cumulativeAmount_;\n\n SafeERC20.safeTransfer(TOKEN, recipient_, claimable_);\n\n emit LogClaimed(recipient_, claimable_, cycle_, fToken_, block.timestamp, block.number);\n }\n}\n" }, "contracts/protocols/lending/merkleDistributor/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Structs {\n struct MerkleCycle {\n // slot 1\n bytes32 merkleRoot;\n // slot 2\n bytes32 merkleContentHash;\n // slot 3\n uint40 cycle;\n uint40 timestamp;\n uint40 publishBlock;\n uint40 startBlock;\n uint40 endBlock;\n }\n}\n" }, "contracts/protocols/lending/merkleDistributor/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Owned } from \"solmate/src/auth/Owned.sol\";\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\n\nimport { Structs } from \"./structs.sol\";\n\nabstract contract Constants {\n IERC20 public constant TOKEN = IERC20(0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb); // INST\n}\n\nabstract contract Variables is Owned, Pausable, Constants, Structs {\n // ------------ storage variables from inherited contracts (Owned, Pausable) come before vars here --------\n\n // ----------------------- slot 0 ---------------------------\n // address public owner; -> from Owned\n\n // bool private _paused; -> from Pausable\n\n // 11 bytes empty\n\n // ----------------------- slot 1 ---------------------------\n\n /// @dev allow list for allowed root proposer addresses\n mapping(address => bool) internal _proposers;\n\n // ----------------------- slot 2-4 ---------------------------\n\n /// @dev merkle root data related to current cycle (proposed and approved).\n /// @dev timestamp & publishBlock = data from last publish.\n // with custom getter to return whole struct at once instead of default solidity getter splitting it into tuple\n MerkleCycle internal _currentMerkleCycle;\n\n // ----------------------- slot 5-7 ---------------------------\n\n /// @dev merkle root data related to pending cycle (proposed but not yet approved).\n /// @dev timestamp & publishBlock = data from last propose.\n // with custom getter to return whole struct at once instead of default solidity getter splitting it into tuple\n MerkleCycle internal _pendingMerkleCycle;\n\n // ----------------------- slot 8 ---------------------------\n\n /// @notice merkle root of the previous cycle\n bytes32 public previousMerkleRoot;\n\n // ----------------------- slot 9 ---------------------------\n\n /// @notice total claimed amount per user address and fToken. user => fToken => claimed amount\n mapping(address => mapping(address => uint256)) public claimed;\n\n constructor(address owner_) Owned(owner_) {}\n}\n" }, "contracts/protocols/lending/stakingRewards/main.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ReentrancyGuard } from \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport { IERC20Permit } from \"@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\nimport { Owned } from \"solmate/src/auth/Owned.sol\";\n\n// Adapted from https://github.com/Uniswap/liquidity-staker/blob/master/contracts/StakingRewards.sol\ncontract FluidLendingStakingRewards is Owned, ReentrancyGuard {\n using SafeERC20 for IERC20;\n\n IERC20 public immutable rewardsToken; // should be INST\n IERC20 public immutable stakingToken; // should be fToken\n uint256 public immutable rewardsDuration; // e.g. 60 days\n\n /* ========== STATE VARIABLES ========== */\n\n // Owned and ReentranyGuard storage variables before\n\n uint256 public periodFinish;\n uint256 public rewardRate;\n uint256 public lastUpdateTime;\n uint256 public rewardPerTokenStored;\n\n mapping(address => uint256) public userRewardPerTokenPaid;\n mapping(address => uint256) public rewards;\n\n uint256 private _totalSupply;\n mapping(address => uint256) private _balances;\n\n /* ========== CONSTRUCTOR ========== */\n\n constructor(address _owner, IERC20 _rewardsToken, IERC20 _stakingToken, uint256 _rewardsDuration) Owned(_owner) {\n require(address(_rewardsToken) != address(0), \"Invalid params\");\n require(address(_stakingToken) != address(0), \"Invalid params\");\n require(_owner != address(0), \"Invalid params\"); // Owned does not have a zero check for owner_\n require(_rewardsDuration > 0, \"Invalid params\");\n\n rewardsToken = _rewardsToken;\n stakingToken = _stakingToken;\n rewardsDuration = _rewardsDuration;\n }\n\n /* ========== VIEWS ========== */\n\n function totalSupply() external view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) external view returns (uint256) {\n return _balances[account];\n }\n\n function lastTimeRewardApplicable() public view returns (uint256) {\n return block.timestamp < periodFinish ? block.timestamp : periodFinish;\n }\n\n function rewardPerToken() public view returns (uint256) {\n if (_totalSupply == 0) {\n return rewardPerTokenStored;\n }\n return\n rewardPerTokenStored + (((lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18) / _totalSupply);\n }\n\n function earned(address account) public view returns (uint256) {\n return (_balances[account] * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18 + rewards[account];\n }\n\n function getRewardForDuration() external view returns (uint256) {\n return rewardRate * rewardsDuration;\n }\n\n /* ========== MUTATIVE FUNCTIONS ========== */\n\n function stakeWithPermit(\n uint256 amount,\n uint deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external nonReentrant updateReward(msg.sender) {\n require(amount > 0, \"Cannot stake 0\");\n\n // permit\n IERC20Permit(address(stakingToken)).permit(msg.sender, address(this), amount, deadline, v, r, s);\n\n stakingToken.safeTransferFrom(msg.sender, address(this), amount);\n\n _totalSupply = _totalSupply + amount;\n _balances[msg.sender] = _balances[msg.sender] + amount;\n\n emit Staked(msg.sender, amount);\n }\n\n function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {\n require(amount > 0, \"Cannot stake 0\");\n\n stakingToken.safeTransferFrom(msg.sender, address(this), amount);\n\n _totalSupply = _totalSupply + amount;\n _balances[msg.sender] = _balances[msg.sender] + amount;\n\n emit Staked(msg.sender, amount);\n }\n\n function withdraw(uint256 amount) public nonReentrant updateReward(msg.sender) {\n require(amount > 0, \"Cannot withdraw 0\");\n\n _totalSupply = _totalSupply - amount;\n _balances[msg.sender] = _balances[msg.sender] - amount;\n\n stakingToken.safeTransfer(msg.sender, amount);\n\n emit Withdrawn(msg.sender, amount);\n }\n\n function getReward() public nonReentrant updateReward(msg.sender) {\n uint256 reward = rewards[msg.sender];\n if (reward > 0) {\n rewards[msg.sender] = 0;\n rewardsToken.safeTransfer(msg.sender, reward);\n emit RewardPaid(msg.sender, reward);\n }\n }\n\n function exit() external {\n withdraw(_balances[msg.sender]);\n getReward();\n }\n\n /* ========== RESTRICTED FUNCTIONS ========== */\n\n function notifyRewardAmount(uint256 reward) external onlyOwner updateReward(address(0)) {\n if (block.timestamp >= periodFinish) {\n rewardRate = reward / rewardsDuration;\n } else {\n uint256 remaining = periodFinish - block.timestamp;\n uint256 leftover = remaining * rewardRate;\n rewardRate = (reward + leftover) / rewardsDuration;\n }\n\n // Ensure the provided reward amount is not more than the balance in the contract.\n // This keeps the reward rate in the right range, preventing overflows due to\n // very high values of rewardRate in the earned and rewardsPerToken functions;\n // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.\n uint balance = rewardsToken.balanceOf(address(this));\n require(rewardRate <= balance / rewardsDuration, \"Provided reward too high\");\n\n lastUpdateTime = block.timestamp;\n periodFinish = block.timestamp + rewardsDuration;\n emit RewardAdded(reward);\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier updateReward(address account) {\n rewardPerTokenStored = rewardPerToken();\n lastUpdateTime = lastTimeRewardApplicable();\n if (account != address(0)) {\n rewards[account] = earned(account);\n userRewardPerTokenPaid[account] = rewardPerTokenStored;\n }\n _;\n }\n\n /* ========== EVENTS ========== */\n\n event RewardAdded(uint256 reward);\n event Staked(address indexed user, uint256 amount);\n event Withdrawn(address indexed user, uint256 amount);\n event RewardPaid(address indexed user, uint256 reward);\n}\n" }, "contracts/protocols/steth/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Error {\n /// @notice general Custom error used for all contract specific errors with the `errorId_` param as error code.\n /// Look up the specific error code in the source code to find a better explanation of what went wrong (errors.sol).\n error StETHQueueError(uint256 errorId_);\n}\n" }, "contracts/protocols/steth/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | StETH | \n |__________________________________*/\n\n /// @notice thrown when maxLTV precent amount is set to 0\n uint256 internal constant StETH__MaxLTVZero = 40001;\n\n /// @notice thrown when the borrow ETH amount to StETH collateral ratio is bigger than the configured `maxLTV`\n uint256 internal constant StETH__MaxLTV = 40002;\n\n /// @notice thrown when an ERC721 other than a Lido Withdrawal NFT is transferred to this contract\n uint256 internal constant StETH__InvalidERC721Transfer = 40003;\n\n /// @notice thrown when an input amount (ethBorrowAmount or stETHAmount) is zero\n uint256 internal constant StETH__InputAmountZero = 40004;\n\n /// @notice thrown when `liquidityCallback` is called, as this protocol only uses native token as borrow asset\n uint256 internal constant StETH__UnexpectedLiquidityCallback = 40005;\n\n /// @notice thrown when there is no claim queued for a claim owner\n uint256 internal constant StETH__NoClaimQueued = 40006;\n\n /// @notice thrown when an unauthorized `msg.sender` calls a protected method\n uint256 internal constant StETH__Unauthorized = 40007;\n\n /// @notice thrown when the borrowAmountRaw is rounded to zero because of the exchange price\n uint256 internal constant StETH__BorrowAmountRawRoundingZero = 40008;\n\n /// @notice thrown when an input address is zero\n uint256 internal constant StETH__AddressZero = 40009;\n\n /// @notice thrown when a reentrancy happens\n uint256 internal constant StETH__Reentrancy = 40010;\n\n /// @notice thrown when maxLTV precent amount is set to >= 100%\n uint256 internal constant StETH__MaxLTVAboveCap = 40011;\n\n /// @notice thrown when renounceOwnership is called\n uint256 internal constant StETH__RenounceOwnershipUnsupported = 40012;\n}\n" }, "contracts/protocols/steth/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Events {\n /// @notice emitted whenever `maxLTV` is updated\n event LogSetMaxLTV(uint256 maxLTV);\n\n /// @notice emitted when a queue() process is executed\n event LogQueue(\n address indexed claimTo,\n uint256 requestIdFrom,\n uint256 borrowETHAmount,\n uint256 queueStETHAmount,\n address borrowTo\n );\n\n /// @notice emitted when a claim() process is executed\n event LogClaim(address indexed claimTo, uint256 requestIdFrom, uint256 claimedAmount, uint256 repayAmount);\n\n /// @notice emitted when an auth is modified by owner\n event LogSetAuth(address indexed auth, bool allowed);\n\n /// @notice emitted when a guardian is modified by owner\n event LogSetGuardian(address indexed guardian, bool allowed);\n\n /// @notice emitted when an allowed user is modified by auths\n event LogSetAllowed(address indexed user, bool allowed);\n\n /// @notice emitted when `allowListActive` status is updated\n event LogSetAllowListActive(bool active);\n\n /// @notice emitted when protocol is paused by guardian\n event LogPaused();\n\n /// @notice emitted when protocol is unpaused by owner\n event LogUnpaused();\n}\n" }, "contracts/protocols/steth/interfaces/external/iLidoWithdrawalQueue.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface ILidoWithdrawalQueue {\n function balanceOf(address) external view returns (uint);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n // code below based on Lido WithdrawalQueueBase.sol\n // see https://github.com/lidofinance/lido-dao/blob/v2.0.0/contracts/0.8.9/WithdrawalQueueBase.sol\n\n /// @notice output format struct for `_getWithdrawalStatus()` method\n struct WithdrawalRequestStatus {\n /// @notice stETH token amount that was locked on withdrawal queue for this request\n uint256 amountOfStETH;\n /// @notice amount of stETH shares locked on withdrawal queue for this request\n uint256 amountOfShares;\n /// @notice address that can claim or transfer this request\n address owner;\n /// @notice timestamp of when the request was created, in seconds\n uint256 timestamp;\n /// @notice true, if request is finalized\n bool isFinalized;\n /// @notice true, if request is claimed. Request is claimable if (isFinalized && !isClaimed)\n bool isClaimed;\n }\n\n /// @notice length of the checkpoint array. Last possible value for the hint.\n /// NB! checkpoints are indexed from 1, so it returns 0 if there is no checkpoints\n function getLastCheckpointIndex() external view returns (uint256);\n\n // code below based on Lido WithdrawalQueue.sol\n // see https://github.com/lidofinance/lido-dao/blob/v2.0.0/contracts/0.8.9/WithdrawalQueue.sol\n\n /// @notice Request the batch of stETH for withdrawal. Approvals for the passed amounts should be done before.\n /// @param _amounts an array of stETH amount values.\n /// The standalone withdrawal request will be created for each item in the passed list.\n /// @param _owner address that will be able to manage the created requests.\n /// If `address(0)` is passed, `msg.sender` will be used as owner.\n /// @return requestIds an array of the created withdrawal request ids\n function requestWithdrawals(\n uint256[] calldata _amounts,\n address _owner\n ) external returns (uint256[] memory requestIds);\n\n /// @notice Claim one`_requestId` request once finalized sending locked ether to the owner\n /// @param _requestId request id to claim\n /// @dev use unbounded loop to find a hint, which can lead to OOG\n /// @dev\n /// Reverts if requestId or hint are not valid\n /// Reverts if request is not finalized or already claimed\n /// Reverts if msg sender is not an owner of request\n function claimWithdrawal(uint256 _requestId) external;\n\n /// @notice Claim a batch of withdrawal requests if they are finalized sending locked ether to the owner\n /// @param _requestIds array of request ids to claim\n /// @param _hints checkpoint hint for each id. Can be obtained with `findCheckpointHints()`\n /// @dev\n /// Reverts if requestIds and hints arrays length differs\n /// Reverts if any requestId or hint in arguments are not valid\n /// Reverts if any request is not finalized or already claimed\n /// Reverts if msg sender is not an owner of the requests\n function claimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external;\n\n //// @notice Returns all withdrawal requests that belongs to the `_owner` address\n ///\n /// WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n /// to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n /// this function has an unbounded cost, and using it as part of a state-changing function may render the function\n /// uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n function getWithdrawalRequests(address _owner) external view returns (uint256[] memory requestsIds);\n\n /// @notice Finds the list of hints for the given `_requestIds` searching among the checkpoints with indices\n /// in the range `[_firstIndex, _lastIndex]`.\n /// NB! Array of request ids should be sorted\n /// NB! `_firstIndex` should be greater than 0, because checkpoint list is 1-based array\n /// Usage: findCheckpointHints(_requestIds, 1, getLastCheckpointIndex())\n /// @param _requestIds ids of the requests sorted in the ascending order to get hints for\n /// @param _firstIndex left boundary of the search range. Should be greater than 0\n /// @param _lastIndex right boundary of the search range. Should be less than or equal to getLastCheckpointIndex()\n /// @return hintIds array of hints used to find required checkpoint for the request\n function findCheckpointHints(\n uint256[] calldata _requestIds,\n uint256 _firstIndex,\n uint256 _lastIndex\n ) external view returns (uint256[] memory hintIds);\n\n /// @notice Returns status for requests with provided ids\n /// @param _requestIds array of withdrawal request ids\n function getWithdrawalStatus(\n uint256[] calldata _requestIds\n ) external view returns (WithdrawalRequestStatus[] memory statuses);\n\n /// @notice maximum amount of stETH that is possible to withdraw by a single request\n /// Prevents accumulating too much funds per single request fulfillment in the future.\n /// @dev To withdraw larger amounts, it's recommended to split it to several requests\n function MAX_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256);\n\n /// @notice minimum amount of stETH that is possible to withdraw by a single request\n function MIN_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256);\n\n // code below based on Lido WithdrawalQueueERC721.sol\n // see https://github.com/lidofinance/lido-dao/blob/v2.0.0/contracts/0.8.9/WithdrawalQueueERC721.sol\n\n /// @notice Finalize requests from last finalized one up to `_lastRequestIdToBeFinalized`\n /// @dev ether to finalize all the requests should be calculated using `prefinalize()` and sent along\n function finalize(uint256 _lastRequestIdToBeFinalized, uint256 _maxShareRate) external payable;\n}\n" }, "contracts/protocols/steth/interfaces/iStETHQueue.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { Structs } from \"../structs.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { ILidoWithdrawalQueue } from \"../interfaces/external/iLidoWithdrawalQueue.sol\";\n\ninterface IFluidStETHQueue {\n /// @notice returns the constant values for LIQUIDITY, LIDO_WITHDRAWAL_QUEUE, STETH\n function constantsView() external view returns (IFluidLiquidity, ILidoWithdrawalQueue, IERC20);\n\n /// @notice gets an open Claim for `claimTo_` and `requestIdFrom_`\n function claims(address claimTo_, uint256 requestIdFrom_) external view returns (Structs.Claim memory);\n\n /// @notice reads if a certain `auth_` address is an allowed auth or not\n function isAuth(address auth_) external view returns (bool);\n\n /// @notice reads if a certain `guardian_` address is an allowed guardian or not\n function isGuardian(address guardian_) external view returns (bool);\n\n /// @notice reads if a certain `user_` address is an allowed user or not\n function isUserAllowed(address user_) external view returns (bool);\n\n /// @notice maximum allowed percentage of LTV (loan-to-value). E.g. 90% -> max. 90 ETH can be borrowed with 100 stETH\n /// as collateral in withdrawal queue. ETH will be received at time of claim to cover the paid borrowed ETH amount.\n /// In 1e2 (1% = 100, 90% = 9_000, 100% = 10_000).\n /// Configurable by auths.\n function maxLTV() external view returns (uint16);\n\n /// @notice flag whether allow list behavior is enabled or not.\n function allowListActive() external view returns (bool);\n\n /// @notice reads if the protocol is paused or not\n function isPaused() external view returns (bool);\n\n /// @notice reads owner address\n function owner() external view returns (address);\n}\n" }, "contracts/protocols/steth/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { UUPSUpgradeable } from \"@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol\";\n\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { ILidoWithdrawalQueue } from \"./interfaces/external/iLidoWithdrawalQueue.sol\";\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { Error } from \"./error.sol\";\nimport { Events } from \"./events.sol\";\nimport { Variables } from \"./variables.sol\";\nimport { LiquidityCalcs } from \"../../libraries/liquidityCalcs.sol\";\n\nabstract contract StETHQueueCore is Variables, Events, Error {\n /// @dev validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert StETHQueueError(ErrorTypes.StETH__AddressZero);\n }\n _;\n }\n\n /// @dev reads the current, updated borrow exchange price for Native ETH at Liquidity\n function _getLiquidityExchangePrice() internal view returns (uint256 borrowExchangePrice_) {\n (, borrowExchangePrice_) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_EXCHANGE_PRICES_SLOT)\n );\n }\n}\n\n/// @dev ReentrancyGuard based on OpenZeppelin implementation.\n/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/security/ReentrancyGuard.sol\nabstract contract ReentrancyGuard is StETHQueueCore {\n uint8 internal constant REENTRANCY_NOT_ENTERED = 1;\n uint8 internal constant REENTRANCY_ENTERED = 2;\n\n constructor() {\n _status = REENTRANCY_ENTERED; // set status to entered on logic contract so only delegateCalls are possible\n }\n\n /// @dev Prevents a contract from calling itself, directly or indirectly.\n /// See OpenZeppelin implementation for more info\n modifier nonReentrant() {\n // On the first call to nonReentrant, _status will be NOT_ENTERED\n if (_status != REENTRANCY_NOT_ENTERED) {\n revert StETHQueueError(ErrorTypes.StETH__Reentrancy);\n }\n\n // Any calls to nonReentrant after this point will fail\n _status = REENTRANCY_ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = REENTRANCY_NOT_ENTERED;\n }\n}\n\n/// @dev FluidStETHQueue admin related methods\nabstract contract StETHQueueAdmin is Variables, ReentrancyGuard {\n /// @dev only auths guard\n modifier onlyAuths() {\n if (!isAuth(msg.sender)) {\n revert StETHQueueError(ErrorTypes.StETH__Unauthorized);\n }\n _;\n }\n\n /// @notice reads if a certain `auth_` address is an allowed auth or not\n function isAuth(address auth_) public view returns (bool) {\n return auth_ == owner() || _auths[auth_] == 1;\n }\n\n /// @notice reads if a certain `guardian_` address is an allowed guardian or not\n function isGuardian(address guardian_) public view returns (bool) {\n return guardian_ == owner() || _guardians[guardian_] == 1;\n }\n\n /// @notice reads if a certain `user_` address is an allowed user or not\n function isUserAllowed(address user_) public view returns (bool) {\n return _allowed[user_] == 1;\n }\n\n /// @notice reads if the protocol is paused or not\n function isPaused() public view returns (bool){\n return _status == REENTRANCY_ENTERED;\n }\n\n /// @notice Sets an address as allowed auth or not. Only callable by owner.\n /// @param auth_ address to set auth value for\n /// @param allowed_ bool flag for whether address is allowed as auth or not\n function setAuth(address auth_, bool allowed_) external onlyOwner validAddress(auth_) {\n _auths[auth_] = allowed_ ? 1 : 0;\n\n emit LogSetAuth(auth_, allowed_);\n }\n\n /// @notice Sets an address as allowed guardian or not. Only callable by owner.\n /// @param guardian_ address to set guardian value for\n /// @param allowed_ bool flag for whether address is allowed as guardian or not\n function setGuardian(address guardian_, bool allowed_) external onlyOwner validAddress(guardian_) {\n _guardians[guardian_] = allowed_ ? 1 : 0;\n\n emit LogSetGuardian(guardian_, allowed_);\n }\n\n /// @notice Sets an address as allowed user or not. Only callable by auths.\n /// @param user_ address to set allowed value for\n /// @param allowed_ bool flag for whether address is allowed as user or not\n function setUserAllowed(address user_, bool allowed_) external onlyAuths validAddress(user_) {\n _allowed[user_] = allowed_ ? 1 : 0;\n\n emit LogSetAllowed(user_, allowed_);\n }\n\n /// @notice Sets `maxLTV` to `maxLTV_` (in 1e2: 1% = 100, 100% = 10000). Must be > 0 and < 100%.\n function setMaxLTV(uint16 maxLTV_) external onlyAuths {\n if (maxLTV_ == 0) {\n revert StETHQueueError(ErrorTypes.StETH__MaxLTVZero);\n }\n if (maxLTV_ >= HUNDRED_PERCENT) {\n revert StETHQueueError(ErrorTypes.StETH__MaxLTVAboveCap);\n }\n\n maxLTV = maxLTV_;\n emit LogSetMaxLTV(maxLTV_);\n }\n\n /// @notice Pauses the protocol (blocks queue() and claim()). Only callable by guardians.\n function pause() external {\n if (!isGuardian(msg.sender)) {\n revert StETHQueueError(ErrorTypes.StETH__Unauthorized);\n }\n\n _status = REENTRANCY_ENTERED;\n\n emit LogPaused();\n }\n\n /// @notice Unpauses the protocol (enables queue() and claim()). Only callable by owner.\n function unpause() external onlyOwner(){\n _status = REENTRANCY_NOT_ENTERED;\n\n emit LogUnpaused();\n }\n\n /// @notice Sets `allowListActive` flag to `status_`. Only callable by owner.\n function setAllowListActive(bool status_) external onlyOwner {\n allowListActive = status_;\n\n emit LogSetAllowListActive(status_);\n }\n}\n\n/// @title StETHQueue\n/// @notice queues an amount of stETH at the Lido WithdrawalQueue, using it as collateral to borrow an amount\n/// of ETH that is paid back when Lido Withdrawal is claimable. Useful e.g. to deleverage a stETH / ETH borrow position.\n/// User target group are whales that want to deleverage stETH / ETH without having to swap (no slippage).\n/// @dev claims are referenced to via the claimTo address and the Lido requestIdFrom, which must be tracked from the moment\n/// of queuing, where it is emitted in the `LogQueue` event, to pass in that information later for `claim()`.\n/// @dev For view methods / accessing data, use the \"StETHResolver\" periphery contract.\n//\n// @dev Note that as a precaution if any claim fails for unforeseen, unexpected reasons, this contract is upgradeable so\n// that Governance could rescue the funds.\n// Note that claiming at Lido Withdrawal Queue is gas-cost-wise cheaper than queueing. So any queue process that passes\n// below block gas limit, also passes at claiming.\ncontract FluidStETHQueue is Variables, StETHQueueCore, StETHQueueAdmin, UUPSUpgradeable {\n constructor(\n IFluidLiquidity liquidity_,\n ILidoWithdrawalQueue lidoWithdrawalQueue_,\n IERC20 stETH_\n )\n validAddress(address(liquidity_))\n validAddress(address(lidoWithdrawalQueue_))\n validAddress(address(stETH_))\n Variables(liquidity_, lidoWithdrawalQueue_, stETH_)\n {\n // ensure logic contract initializer is not abused by disabling initializing\n // see https://forum.openzeppelin.com/t/security-advisory-initialize-uups-implementation-contracts/15301\n // and https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract\n _disableInitializers();\n }\n\n /// @notice initializes the contract with `owner_` as owner\n function initialize(address owner_) public initializer validAddress(owner_) {\n _transferOwnership(owner_);\n\n // approve infinite stETH to Lido withdrawal queue for requesting withdrawals\n SafeERC20.safeApprove(STETH, address(LIDO_WITHDRAWAL_QUEUE), type(uint256).max);\n\n _status = REENTRANCY_NOT_ENTERED; // set reentrancy to not entered on proxy\n\n allowListActive = true; // start protocol in a protected state with allow list being active\n\n // Borrow a minor dust amount of ETH (`DUST_BORROW_AMOUNT`) from Liquidity to avoid any potential reverts\n // because of rounding differences etc\n LIQUIDITY.operate(NATIVE_TOKEN_ADDRESS, 0, int256(DUST_BORROW_AMOUNT), address(0), address(this), new bytes(0));\n }\n\n receive() external payable {}\n\n function _authorizeUpgrade(address) internal override onlyOwner {}\n\n /// @notice override renounce ownership as it could leave the contract in an unwanted state if called by mistake.\n function renounceOwnership() public view override onlyOwner {\n revert StETHQueueError(ErrorTypes.StETH__RenounceOwnershipUnsupported);\n }\n\n /// @notice queues an amount of stETH at the Lido WithdrawalQueue, using it as collateral to borrow an amount\n /// of ETH from Liquidity that is paid back when Lido Withdrawal is claimable, triggered with `claim()`.\n /// @dev if `allowListActive` == true, then only allowed users can call this method.\n /// @param ethBorrowAmount_ amount of ETH to borrow and send to `borrowTo_`\n /// @param stETHAmount_ amount of stETH to queue at Lido Withdrawal Queue and use as collateral\n /// @param borrowTo_ receiver of the `ethBorrowAmount_`\n /// @param claimTo_ receiver of the left over stETH funds at time of `claim()`\n /// @return requestIdFrom_ first request id at Lido withdrawal queue. Used to identify claims\n function queue(\n uint256 ethBorrowAmount_,\n uint256 stETHAmount_,\n address borrowTo_,\n address claimTo_\n ) public nonReentrant validAddress(borrowTo_) validAddress(claimTo_) returns (uint256 requestIdFrom_) {\n if (allowListActive && !isUserAllowed(msg.sender)) {\n revert StETHQueueError(ErrorTypes.StETH__Unauthorized);\n }\n\n // 1. sanity checks\n if (ethBorrowAmount_ == 0 || stETHAmount_ == 0) {\n revert StETHQueueError(ErrorTypes.StETH__InputAmountZero);\n }\n // validity check ltv of borrow amount / collateral is below configured maxLTV\n if ((ethBorrowAmount_ * HUNDRED_PERCENT) / stETHAmount_ > maxLTV) {\n revert StETHQueueError(ErrorTypes.StETH__MaxLTV);\n }\n\n // 2. get `stETHAmount_` from msg.sender. must be approved to this contract\n SafeERC20.safeTransferFrom(STETH, msg.sender, address(this), stETHAmount_);\n\n // 3. queue stETH withdrawal at Lido, receive withdrawal NFT\n uint256[] memory amounts_;\n {\n // maximum amount of stETH that is possible to withdraw by a single request at Lido (should be 1_000 stETH).\n uint256 maxStETHWithdrawalAmount_ = LIDO_WITHDRAWAL_QUEUE.MAX_STETH_WITHDRAWAL_AMOUNT();\n // minimum amount of stETH that is possible to withdraw by a single request at Lido (should be 100 wei).\n uint256 minStETHWithdrawalAmount_ = LIDO_WITHDRAWAL_QUEUE.MIN_STETH_WITHDRAWAL_AMOUNT();\n\n if (stETHAmount_ > maxStETHWithdrawalAmount_) {\n // if withdraw amount is > MAX_STETH_WITHDRAWAL_AMOUNT it must be split into multiple smaller amounts\n // each of maximum MAX_STETH_WITHDRAWAL_AMOUNT\n\n bool lastAmountExact_;\n uint256 fullAmountsLength_ = stETHAmount_ / maxStETHWithdrawalAmount_;\n unchecked {\n // check if remainder for last amount in array is exactly matching MAX_STETH_WITHDRAWAL_AMOUNT\n lastAmountExact_ = stETHAmount_ % maxStETHWithdrawalAmount_ == 0;\n // total elements are count of full amounts + 1 (unless lastAmountExact_ is true)\n amounts_ = new uint256[](fullAmountsLength_ + (lastAmountExact_ ? 0 : 1));\n }\n // fill amounts array with MAX_STETH_WITHDRAWAL_AMOUNT except for last element\n for (uint256 i; i < fullAmountsLength_; ) {\n amounts_[i] = maxStETHWithdrawalAmount_;\n\n unchecked {\n ++i;\n }\n }\n\n if (!lastAmountExact_) {\n // last element is result of modulo operation so length of array is fullAmountsLength_ +1\n amounts_[fullAmountsLength_] = stETHAmount_ % maxStETHWithdrawalAmount_;\n\n if (amounts_[fullAmountsLength_] < minStETHWithdrawalAmount_) {\n // make sure remainder amount for last element in array is at least MIN_STETH_WITHDRAWAL_AMOUNT.\n // if smaller, deduct MIN_STETH_WITHDRAWAL_AMOUNT from the second last element and it to the last.\n unchecked {\n amounts_[fullAmountsLength_ - 1] -= minStETHWithdrawalAmount_;\n amounts_[fullAmountsLength_] += minStETHWithdrawalAmount_;\n }\n }\n }\n } else {\n amounts_ = new uint256[](1);\n amounts_[0] = stETHAmount_;\n }\n }\n\n // request withdrawal at Lido, receiving the withdrawal NFT to this contract as owner\n uint256[] memory requestIds_ = LIDO_WITHDRAWAL_QUEUE.requestWithdrawals(amounts_, address(this));\n\n requestIdFrom_ = requestIds_[0];\n\n // 4. borrow amount of ETH from Liquidity and send it to msg.sender.\n // sender can use this to e.g. pay back a flashloan used to deleverage at a borrowing protocol.\n (, uint256 borrowExchangePrice_) = LIQUIDITY.operate(\n NATIVE_TOKEN_ADDRESS,\n 0,\n int256(ethBorrowAmount_),\n address(0),\n borrowTo_,\n new bytes(0)\n );\n\n uint256 borrowAmountRaw_ = (ethBorrowAmount_ * EXCHANGE_PRICES_PRECISION) / borrowExchangePrice_;\n if (borrowAmountRaw_ == 0) {\n revert StETHQueueError(ErrorTypes.StETH__BorrowAmountRawRoundingZero);\n }\n\n // 5. write linked claim data in storage\n claims[claimTo_][requestIdFrom_] = Claim({\n // storing borrow amount in raw to account for borrow interest that must be paid back at `claim()` time.\n borrowAmountRaw: uint128(borrowAmountRaw_),\n checkpoint: uint48(LIDO_WITHDRAWAL_QUEUE.getLastCheckpointIndex()),\n requestIdTo: uint40(requestIds_[requestIds_.length - 1])\n });\n\n // 6. emit event\n emit LogQueue(claimTo_, requestIdFrom_, ethBorrowAmount_, stETHAmount_, borrowTo_);\n }\n\n /// @notice claims all open requests at LidoWithdrawalQueue for `claimTo_`, repays the borrowed ETH amount at Liquidity\n /// and sends the rest of funds to `claimTo_`.\n /// @param claimTo_ claimTo receiver to process the claim for\n /// @param requestIdFrom_ Lido requestId from (start), as emitted at time of queuing (`queue()`) via `LogQueue`\n /// @return claimedAmount_ total amount of claimed stETH\n /// @return repayAmount_ total repaid ETH amount at Liquidity\n function claim(\n address claimTo_,\n uint256 requestIdFrom_\n ) public nonReentrant returns (uint256 claimedAmount_, uint256 repayAmount_) {\n Claim memory claim_ = claims[claimTo_][requestIdFrom_];\n\n if (claim_.checkpoint == 0) {\n // this implicitly confirms input params claimTo_ and requestIdFrom_ are valid, as a claim was found.\n revert StETHQueueError(ErrorTypes.StETH__NoClaimQueued);\n }\n\n // store snapshot of balance before claiming\n claimedAmount_ = address(this).balance;\n\n // 1. claim all requests at Lido. This will burn the NFTs.\n uint256 requestsLength_ = claim_.requestIdTo - requestIdFrom_ + 1;\n if (requestsLength_ == 1) {\n // only one request id\n LIDO_WITHDRAWAL_QUEUE.claimWithdrawal(claim_.requestIdTo);\n } else {\n uint256 curRequest_ = requestIdFrom_;\n\n // build requestIds array from `requestIdFrom` to `requestIdTo`\n uint256[] memory requestIds_ = new uint256[](requestsLength_);\n for (uint256 i; i < requestsLength_; ) {\n requestIds_[i] = curRequest_;\n\n unchecked {\n ++i;\n ++curRequest_;\n }\n }\n // claim withdrawals at Lido queue\n LIDO_WITHDRAWAL_QUEUE.claimWithdrawals(\n requestIds_,\n LIDO_WITHDRAWAL_QUEUE.findCheckpointHints(\n requestIds_,\n claim_.checkpoint,\n LIDO_WITHDRAWAL_QUEUE.getLastCheckpointIndex()\n )\n );\n }\n\n claimedAmount_ = address(this).balance - claimedAmount_;\n\n // 2. calculate borrowed amount to repay after interest with updated exchange price from Liquidity\n // round up for safe rounding\n repayAmount_ = ((claim_.borrowAmountRaw * _getLiquidityExchangePrice()) / EXCHANGE_PRICES_PRECISION) + 1;\n\n // 3. repay borrow amount at Liquidity. reverts if claimed amount does not cover borrowed amount.\n LIQUIDITY.operate{ value: repayAmount_ }(\n NATIVE_TOKEN_ADDRESS,\n 0,\n -int256(repayAmount_),\n address(0),\n address(0),\n new bytes(0) // not needed for native token\n );\n\n // 4. pay out rest of balance to owner\n Address.sendValue(payable(claimTo_), claimedAmount_ - repayAmount_);\n\n // 5. delete mapping\n delete claims[claimTo_][requestIdFrom_];\n\n // 6. emit event\n emit LogClaim(claimTo_, requestIdFrom_, claimedAmount_, repayAmount_);\n }\n\n /// @notice accept ERC721 token transfers ONLY from LIDO_WITHDRAWAL_QUEUE\n function onERC721Received(\n address /** operator_ */,\n address /** from_ */,\n uint256 /** tokenId_ */,\n bytes memory /** data_ */\n ) public view returns (bytes4) {\n if (msg.sender == address(LIDO_WITHDRAWAL_QUEUE)) {\n return this.onERC721Received.selector;\n }\n\n revert StETHQueueError(ErrorTypes.StETH__InvalidERC721Transfer);\n }\n\n /// @notice liquidityCallback as used by Liquidity -> But unsupported in this contract as it only ever uses native\n /// token as borrowed asset, which is repaid directly via `msg.value`. Always reverts.\n function liquidityCallback(\n address /** token_ */,\n uint256 /** amount_ */,\n bytes calldata /** data_ */\n ) external pure {\n revert StETHQueueError(ErrorTypes.StETH__UnexpectedLiquidityCallback);\n }\n}\n" }, "contracts/protocols/steth/proxy.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { ERC1967Proxy } from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\n\n/// @title FluidStETHQueueProxy\n/// @notice Default ERC1967Proxy for StETHQueue\ncontract FluidStETHQueueProxy is ERC1967Proxy {\n constructor(address logic_, bytes memory data_) payable ERC1967Proxy(logic_, data_) {}\n}\n" }, "contracts/protocols/steth/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Structs {\n /// @notice Claim data struct for storage link to owner address\n struct Claim {\n /// @param borrowAmountRaw raw borrow amount at Liquidity. Multiply with borrowExchangePrice to get normal borrow amount\n uint128 borrowAmountRaw;\n /// @param checkpoint checkpoint at time of queue. optimizes finding checkpoint hints range at claim time.\n uint48 checkpoint;\n /// @param requestIdTo last request Id linked to the claim\n uint40 requestIdTo;\n // 5 bytes empty\n }\n}\n" }, "contracts/protocols/steth/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Initializable } from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport { OwnableUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\n\nimport { LiquiditySlotsLink } from \"../../libraries/liquiditySlotsLink.sol\";\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\nimport { ILidoWithdrawalQueue } from \"./interfaces/external/iLidoWithdrawalQueue.sol\";\nimport { Structs } from \"./structs.sol\";\n\nabstract contract Constants {\n /// @dev hundred percent at 1e2 precision\n uint256 internal constant HUNDRED_PERCENT = 1e4;\n\n /// @dev precision for exchange prices in Liquidity\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev address that is mapped to the chain native token at Liquidity\n address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @dev dust amount to borrow from Liquidity at initialize()\n uint256 internal constant DUST_BORROW_AMOUNT = 1e12;\n\n /// @notice address of the Liquidity contract.\n IFluidLiquidity internal immutable LIQUIDITY;\n\n /// @notice address of the Lido Withdrawal Queue contract (0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1)\n ILidoWithdrawalQueue internal immutable LIDO_WITHDRAWAL_QUEUE;\n\n /// @notice address of the StETH contract.\n IERC20 internal immutable STETH;\n\n /// @dev slot id in Liquidity contract for exchange prices storage slot for NATIVE_TOKEN_ADDRESS.\n bytes32 internal immutable LIQUIDITY_EXCHANGE_PRICES_SLOT;\n\n constructor(IFluidLiquidity liquidity_, ILidoWithdrawalQueue lidoWithdrawalQueue_, IERC20 stETH_) {\n LIQUIDITY = liquidity_;\n\n LIDO_WITHDRAWAL_QUEUE = lidoWithdrawalQueue_;\n\n STETH = stETH_;\n\n LIQUIDITY_EXCHANGE_PRICES_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n NATIVE_TOKEN_ADDRESS\n );\n }\n\n /// @notice returns the constant values for LIQUIDITY, LIDO_WITHDRAWAL_QUEUE, STETH\n function constantsView() external view returns (IFluidLiquidity, ILidoWithdrawalQueue, IERC20) {\n return (LIQUIDITY, LIDO_WITHDRAWAL_QUEUE, STETH);\n }\n}\n\nabstract contract Variables is Initializable, OwnableUpgradeable, Constants, Structs {\n // ------------ storage variables from inherited contracts (Initializable, OwnableUpgradeable) come before vars here --------\n // @dev variables here start at storage slot 101, before is:\n // - Initializable with storage slot 0:\n // uint8 private _initialized;\n // bool private _initializing;\n // - OwnableUpgradeable with slots 1 to 100:\n // uint256[50] private __gap; (from ContextUpgradeable, slot 1 until slot 50)\n // address private _owner; (at slot 51)\n // uint256[49] private __gap; (slot 52 until slot 100)\n\n // ----------------------- slot 101 ---------------------------\n\n /// @notice maps claimTo address and requestIdFrom to the Claim struct containing necessary information for executing the claim process.\n mapping(address => mapping(uint256 => Claim)) public claims;\n\n // ----------------------- slot 102 ---------------------------\n\n /// @dev status for reentrancy guard\n uint8 internal _status;\n\n /// @notice maximum allowed percentage of LTV (loan-to-value). E.g. 90% -> max. 90 ETH can be borrowed with 100 stETH\n /// as collateral in withdrawal queue. ETH will be received at time of claim to cover the paid borrowed ETH amount.\n /// In 1e2 (1% = 100, 90% = 9_000, 100% = 10_000).\n /// Configurable by auths.\n uint16 public maxLTV;\n\n /// @notice flag whether allow list behavior is enabled or not.\n bool public allowListActive;\n\n // 28 bytes free\n\n // ----------------------- slot 103 ---------------------------\n /// @dev auths can update maxLTV.\n /// owner can add/remove auths.\n /// Owner is auth by default.\n mapping(address => uint256) internal _auths;\n\n // ----------------------- slot 104 ---------------------------\n /// @dev guardians can pause/unpause queue() and claim().\n /// owner can add/remove guardians.\n /// Owner is guardian by default.\n mapping(address => uint256) internal _guardians;\n\n // ----------------------- slot 105 ---------------------------\n /// @dev allowed users can use the StETH protocol (if `allowListActive` is true, then use is open for everyone).\n /// owner and auths can add/remove allowed users.\n mapping(address => uint256) internal _allowed;\n\n constructor(\n IFluidLiquidity liquidity_,\n ILidoWithdrawalQueue lidoWithdrawalQueue_,\n IERC20 stETH_\n ) Constants(liquidity_, lidoWithdrawalQueue_, stETH_) {}\n}\n" }, "contracts/protocols/vault/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Error {\n error FluidVaultError(uint256 errorId_);\n\n /// @notice used to simulate liquidation to find the maximum liquidatable amounts\n error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated);\n}\n" }, "contracts/protocols/vault/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | Vault Factory | \n |__________________________________*/\n\n uint256 internal constant VaultFactory__InvalidOperation = 30001;\n uint256 internal constant VaultFactory__Unauthorized = 30002;\n uint256 internal constant VaultFactory__SameTokenNotAllowed = 30003;\n uint256 internal constant VaultFactory__InvalidParams = 30004;\n uint256 internal constant VaultFactory__InvalidVault = 30005;\n uint256 internal constant VaultFactory__InvalidVaultAddress = 30006;\n uint256 internal constant VaultFactory__OnlyDelegateCallAllowed = 30007;\n\n /***********************************|\n | VaultT1 | \n |__________________________________*/\n\n /// @notice thrown at reentrancy\n uint256 internal constant VaultT1__AlreadyEntered = 31001;\n\n /// @notice thrown when user sends deposit & borrow amount as 0\n uint256 internal constant VaultT1__InvalidOperateAmount = 31002;\n\n /// @notice thrown when msg.value is not in sync with native token deposit or payback\n uint256 internal constant VaultT1__InvalidMsgValueOperate = 31003;\n\n /// @notice thrown when msg.sender is not the owner of the vault\n uint256 internal constant VaultT1__NotAnOwner = 31004;\n\n /// @notice thrown when user's position does not exist. Sending the wrong index from the frontend\n uint256 internal constant VaultT1__TickIsEmpty = 31005;\n\n /// @notice thrown when the user's position is above CF and the user tries to make it more risky by trying to withdraw or borrow\n uint256 internal constant VaultT1__PositionAboveCF = 31006;\n\n /// @notice thrown when the top tick is not initialized. Happens if the vault is totally new or all the user's left\n uint256 internal constant VaultT1__TopTickDoesNotExist = 31007;\n\n /// @notice thrown when msg.value in liquidate is not in sync payback\n uint256 internal constant VaultT1__InvalidMsgValueLiquidate = 31008;\n\n /// @notice thrown when slippage is more on liquidation than what the liquidator sent\n uint256 internal constant VaultT1__ExcessSlippageLiquidation = 31009;\n\n /// @notice thrown when msg.sender is not the rebalancer/reserve contract\n uint256 internal constant VaultT1__NotRebalancer = 31010;\n\n /// @notice thrown when NFT of one vault interacts with the NFT of other vault\n uint256 internal constant VaultT1__NftNotOfThisVault = 31011;\n\n /// @notice thrown when the token is not initialized on the liquidity contract\n uint256 internal constant VaultT1__TokenNotInitialized = 31012;\n\n /// @notice thrown when admin updates fallback if a non-auth calls vault\n uint256 internal constant VaultT1__NotAnAuth = 31013;\n\n /// @notice thrown in operate when user tries to witdhraw more collateral than deposited\n uint256 internal constant VaultT1__ExcessCollateralWithdrawal = 31014;\n\n /// @notice thrown in operate when user tries to payback more debt than borrowed\n uint256 internal constant VaultT1__ExcessDebtPayback = 31015;\n\n /// @notice thrown when user try to withdrawal more than operate's withdrawal limit\n uint256 internal constant VaultT1__WithdrawMoreThanOperateLimit = 31016;\n\n /// @notice thrown when caller of liquidityCallback is not Liquidity\n uint256 internal constant VaultT1__InvalidLiquidityCallbackAddress = 31017;\n\n /// @notice thrown when reentrancy is not already on\n uint256 internal constant VaultT1__NotEntered = 31018;\n\n /// @notice thrown when someone directly calls secondary implementation contract\n uint256 internal constant VaultT1__OnlyDelegateCallAllowed = 31019;\n\n /// @notice thrown when the safeTransferFrom for a token amount failed\n uint256 internal constant VaultT1__TransferFromFailed = 31020;\n\n /// @notice thrown when exchange price overflows while updating on storage\n uint256 internal constant VaultT1__ExchangePriceOverFlow = 31021;\n\n /// @notice thrown when debt to liquidate amt is sent wrong\n uint256 internal constant VaultT1__InvalidLiquidationAmt = 31022;\n\n /// @notice thrown when user debt or collateral goes above 2**128 or below -2**128\n uint256 internal constant VaultT1__UserCollateralDebtExceed = 31023;\n\n /// @notice thrown if on liquidation branch debt becomes lower than 100\n uint256 internal constant VaultT1__BranchDebtTooLow = 31024;\n\n /// @notice thrown when tick's debt is less than 10000\n uint256 internal constant VaultT1__TickDebtTooLow = 31025;\n\n /// @notice thrown when the received new liquidity exchange price is of unexpected value (< than the old one)\n uint256 internal constant VaultT1__LiquidityExchangePriceUnexpected = 31026;\n\n /// @notice thrown when user's debt is less than 10000\n uint256 internal constant VaultT1__UserDebtTooLow = 31027;\n\n /// @notice thrown when on only payback and only deposit the ratio of position increases\n uint256 internal constant VaultT1__InvalidPaybackOrDeposit = 31028;\n\n /// @notice thrown when liquidation just happens of a single partial or when there's nothing to liquidate\n uint256 internal constant VaultT1__InvalidLiquidation = 31029;\n\n /// @notice thrown when msg.value is sent wrong in rebalance\n uint256 internal constant VaultT1__InvalidMsgValueInRebalance = 31030;\n\n /// @notice thrown when nothing rebalanced\n uint256 internal constant VaultT1__NothingToRebalance = 31031;\n\n /// @notice thrown on unforseen liquidation scenarios. Might never come in use.\n uint256 internal constant VaultT1__LiquidationReverts = 31032;\n\n /// @notice thrown when oracle price is > 1e54\n uint256 internal constant VaultT1__InvalidOraclePrice = 31033;\n\n /***********************************|\n | ERC721 | \n |__________________________________*/\n\n uint256 internal constant ERC721__InvalidParams = 32001;\n uint256 internal constant ERC721__Unauthorized = 32002;\n uint256 internal constant ERC721__InvalidOperation = 32003;\n uint256 internal constant ERC721__UnsafeRecipient = 32004;\n uint256 internal constant ERC721__OutOfBoundsIndex = 32005;\n\n /***********************************|\n | Vault Admin | \n |__________________________________*/\n\n /// @notice thrown when admin tries to setup invalid value which are crossing limits\n uint256 internal constant VaultT1Admin__ValueAboveLimit = 33001;\n\n /// @notice when someone directly calls admin implementation contract\n uint256 internal constant VaultT1Admin__OnlyDelegateCallAllowed = 33002;\n\n /// @notice thrown when auth sends NFT ID as 0 while collecting dust debt\n uint256 internal constant VaultT1Admin__NftIdShouldBeNonZero = 33003;\n\n /// @notice thrown when trying to collect dust debt of NFT which is not of this vault\n uint256 internal constant VaultT1Admin__NftNotOfThisVault = 33004;\n\n /// @notice thrown when dust debt of NFT is 0, meaning nothing to collect\n uint256 internal constant VaultT1Admin__DustDebtIsZero = 33005;\n\n /// @notice thrown when final debt after liquidation is not 0, meaning position 100% liquidated\n uint256 internal constant VaultT1Admin__FinalDebtShouldBeZero = 33006;\n\n /// @notice thrown when NFT is not liquidated state\n uint256 internal constant VaultT1Admin__NftNotLiquidated = 33007;\n\n /// @notice thrown when total absorbed dust debt is 0\n uint256 internal constant VaultT1Admin__AbsorbedDustDebtIsZero = 33008;\n\n /// @notice thrown when address is set as 0\n uint256 internal constant VaultT1Admin__AddressZeroNotAllowed = 33009;\n\n /***********************************|\n | Vault Rewards | \n |__________________________________*/\n\n uint256 internal constant VaultRewards__Unauthorized = 34001;\n uint256 internal constant VaultRewards__AddressZero = 34002;\n uint256 internal constant VaultRewards__InvalidParams = 34003;\n uint256 internal constant VaultRewards__NewMagnifierSameAsOldMagnifier = 34004;\n uint256 internal constant VaultRewards__NotTheInitiator = 34005;\n uint256 internal constant VaultRewards__AlreadyStarted = 34006;\n uint256 internal constant VaultRewards__RewardsNotStartedOrEnded = 34007;\n}\n" }, "contracts/protocols/vault/factory/deploymentLogics/vaultT1Logic.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { SSTORE2 } from \"solmate/src/utils/SSTORE2.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { Error } from \"../../error.sol\";\nimport { IFluidVaultFactory } from \"../../interfaces/iVaultFactory.sol\";\n\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\n\nimport { IFluidVaultT1 } from \"../../interfaces/iVaultT1.sol\";\nimport { FluidVaultT1 } from \"../../vaultT1/coreModule/main.sol\";\n\ninterface IERC20 {\n function decimals() external view returns (uint8);\n}\n\ncontract FluidVaultT1DeploymentLogic is Error {\n address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @dev SSTORE2 pointer for the VaultT1 creation code. Stored externally to reduce factory bytecode (in 2 parts)\n address internal immutable VAULT_T1_CREATIONCODE_ADDRESS_1;\n address internal immutable VAULT_T1_CREATIONCODE_ADDRESS_2;\n\n /// @notice address of liquidity contract\n address public immutable LIQUIDITY;\n\n /// @notice address of Admin implementation\n address public immutable ADMIN_IMPLEMENTATION;\n\n /// @notice address of Secondary implementation\n address public immutable SECONDARY_IMPLEMENTATION;\n\n /// @notice address of this contract\n address public immutable ADDRESS_THIS;\n\n /// @notice Emitted when a new vaultT1 is deployed.\n /// @param vault The address of the newly deployed vault.\n /// @param vaultId The id of the newly deployed vault.\n /// @param supplyToken The address of the supply token.\n /// @param borrowToken The address of the borrow token.\n event VaultT1Deployed(\n address indexed vault,\n uint256 vaultId,\n address indexed supplyToken,\n address indexed borrowToken\n );\n\n constructor(address liquidity_, address vaultAdminImplementation_, address vaultSecondaryImplementation_) {\n LIQUIDITY = liquidity_;\n ADMIN_IMPLEMENTATION = vaultAdminImplementation_;\n SECONDARY_IMPLEMENTATION = vaultSecondaryImplementation_;\n\n // split storing creation code into two SSTORE2 pointers, because:\n // due to contract code limits 24576 bytes is the maximum amount of data that can be written in a single pointer / key.\n // Attempting to write more will result in failure.\n // So by splitting in two parts we can make sure that the contract bytecode size can use up the full limit of 24576 bytes.\n uint256 creationCodeLength_ = type(FluidVaultT1).creationCode.length;\n VAULT_T1_CREATIONCODE_ADDRESS_1 = SSTORE2.write(\n _bytesSlice(type(FluidVaultT1).creationCode, 0, creationCodeLength_ / 2)\n );\n // slice lengths:\n // when even length, e.g. 250:\n // part 1 = 0 -> 250 / 2, so 0 until 125 length, so 0 -> 125\n // part 2 = 250 / 2 -> 250 - 250 / 2, so 125 until 125 length, so 125 -> 250\n // when odd length: e.g. 251:\n // part 1 = 0 -> 251 / 2, so 0 until 125 length, so 0 -> 125\n // part 2 = 251 / 2 -> 251 - 251 / 2, so 125 until 126 length, so 125 -> 251\n VAULT_T1_CREATIONCODE_ADDRESS_2 = SSTORE2.write(\n _bytesSlice(\n type(FluidVaultT1).creationCode,\n creationCodeLength_ / 2,\n creationCodeLength_ - creationCodeLength_ / 2\n )\n );\n\n ADDRESS_THIS = address(this);\n }\n\n /// @notice Computes vaultT1 bytecode for the given supply token (`supplyToken_`) and borrow token (`borrowToken_`).\n /// This will be called by the VaultFactory via .delegateCall\n /// @param supplyToken_ The address of the supply token.\n /// @param borrowToken_ The address of the borrow token.\n /// @return vaultCreationBytecode_ Returns the bytecode of the new vault to deploy.\n function vaultT1(\n address supplyToken_,\n address borrowToken_\n ) external returns (bytes memory vaultCreationBytecode_) {\n if (address(this) == ADDRESS_THIS) revert FluidVaultError(ErrorTypes.VaultFactory__OnlyDelegateCallAllowed);\n\n if (supplyToken_ == borrowToken_) revert FluidVaultError(ErrorTypes.VaultFactory__SameTokenNotAllowed);\n\n IFluidVaultT1.ConstantViews memory constants_;\n constants_.liquidity = LIQUIDITY;\n constants_.factory = address(this);\n constants_.adminImplementation = ADMIN_IMPLEMENTATION;\n constants_.secondaryImplementation = SECONDARY_IMPLEMENTATION;\n constants_.supplyToken = supplyToken_;\n constants_.supplyDecimals = supplyToken_ != NATIVE_TOKEN ? IERC20(supplyToken_).decimals() : 18;\n constants_.borrowToken = borrowToken_;\n constants_.borrowDecimals = borrowToken_ != NATIVE_TOKEN ? IERC20(borrowToken_).decimals() : 18;\n constants_.vaultId = IFluidVaultFactory(address(this)).totalVaults();\n\n address vault_ = IFluidVaultFactory(address(this)).getVaultAddress(constants_.vaultId);\n\n constants_ = _calculateLiquidityVaultSlots(constants_, vault_);\n\n vaultCreationBytecode_ = abi.encodePacked(vaultT1CreationBytecode(), abi.encode(constants_));\n\n emit VaultT1Deployed(vault_, constants_.vaultId, supplyToken_, borrowToken_);\n\n return vaultCreationBytecode_;\n }\n\n /// @notice returns the stored VaultT1 creation bytecode\n function vaultT1CreationBytecode() public view returns (bytes memory) {\n return\n _bytesConcat(SSTORE2.read(VAULT_T1_CREATIONCODE_ADDRESS_1), SSTORE2.read(VAULT_T1_CREATIONCODE_ADDRESS_2));\n }\n\n /// @dev Calculates the liquidity vault slots for the given supply token, borrow token, and vault (`vault_`).\n /// @param constants_ Constants struct as used in Vault T1\n /// @param vault_ The address of the vault.\n /// @return liquidityVaultSlots_ Returns the calculated liquidity vault slots set in the `IFluidVaultT1.ConstantViews` struct.\n function _calculateLiquidityVaultSlots(\n IFluidVaultT1.ConstantViews memory constants_,\n address vault_\n ) private pure returns (IFluidVaultT1.ConstantViews memory) {\n constants_.liquiditySupplyExchangePriceSlot = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n constants_.supplyToken\n );\n constants_.liquidityBorrowExchangePriceSlot = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n constants_.borrowToken\n );\n constants_.liquidityUserSupplySlot = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT,\n vault_,\n constants_.supplyToken\n );\n constants_.liquidityUserBorrowSlot = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT,\n vault_,\n constants_.borrowToken\n );\n return constants_;\n }\n\n // @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\n function _bytesConcat(bytes memory _preBytes, bytes memory _postBytes) private pure returns (bytes memory) {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(\n 0x40,\n and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n )\n )\n }\n\n return tempBytes;\n }\n\n // @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\n function _bytesSlice(bytes memory _bytes, uint256 _start, uint256 _length) private pure returns (bytes memory) {\n require(_length + 31 >= _length, \"slice_overflow\");\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n}\n" }, "contracts/protocols/vault/factory/ERC721/ERC721.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { Error } from \"../../error.sol\";\n\n/// @notice Fluid Vault Factory ERC721 base contract. Implements the ERC721 standard, based on Solmate.\n/// In addition, implements ERC721 Enumerable.\n/// Modern, minimalist, and gas efficient ERC-721 with Enumerable implementation.\n///\n/// @author Instadapp\n/// @author Modified Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)\nabstract contract ERC721 is Error {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event Transfer(address indexed from, address indexed to, uint256 indexed id);\n\n event Approval(address indexed owner, address indexed spender, uint256 indexed id);\n\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /*//////////////////////////////////////////////////////////////\n METADATA STORAGE/LOGIC\n //////////////////////////////////////////////////////////////*/\n\n string public name;\n\n string public symbol;\n\n function tokenURI(uint256 id) public view virtual returns (string memory);\n\n /*//////////////////////////////////////////////////////////////\n ERC721 BALANCE/OWNER STORAGE\n //////////////////////////////////////////////////////////////*/\n\n // token id => token config\n // uint160 0 - 159: address:: owner\n // uint32 160 - 191: uint32:: index\n // uint32 192 - 223: uint32:: vaultId\n // uint32 224 - 255: uint32:: null\n mapping(uint256 => uint256) internal _tokenConfig;\n\n // owner => slot => index\n /*\n // slot 0: \n // uint32 0 - 31: uint32:: balanceOf\n // uint224 32 - 255: 7 tokenIds each of uint32 packed\n // slot N (N >= 1)\n // uint32 * 8 each tokenId\n */\n mapping(address => mapping(uint256 => uint256)) internal _ownerConfig;\n\n /// @notice returns `owner_` of NFT with `id_`\n function ownerOf(uint256 id_) public view virtual returns (address owner_) {\n if ((owner_ = address(uint160(_tokenConfig[id_]))) == address(0))\n revert FluidVaultError(ErrorTypes.ERC721__InvalidParams);\n }\n\n /// @notice returns total count of NFTs owned by `owner_`\n function balanceOf(address owner_) public view virtual returns (uint256) {\n if (owner_ == address(0)) revert FluidVaultError(ErrorTypes.ERC721__InvalidParams);\n\n return _ownerConfig[owner_][0] & type(uint32).max;\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC721Enumerable STORAGE\n //////////////////////////////////////////////////////////////*/\n\n /// @notice total amount of tokens stored by the contract.\n uint256 public totalSupply;\n\n /*//////////////////////////////////////////////////////////////\n ERC721 APPROVAL STORAGE\n //////////////////////////////////////////////////////////////*/\n\n /// @notice trackes if a NFT id is approved for a certain address.\n mapping(uint256 => address) public getApproved;\n\n /// @notice trackes if all the NFTs of an owner are approved for a certain other address.\n mapping(address => mapping(address => bool)) public isApprovedForAll;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(string memory _name, string memory _symbol) {\n name = _name;\n symbol = _symbol;\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC721 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /// @notice approves an NFT with `id_` to be spent (transferred) by `spender_`\n function approve(address spender_, uint256 id_) public virtual {\n address owner_ = address(uint160(_tokenConfig[id_]));\n if (!(msg.sender == owner_ || isApprovedForAll[owner_][msg.sender]))\n revert FluidVaultError(ErrorTypes.ERC721__Unauthorized);\n\n getApproved[id_] = spender_;\n\n emit Approval(owner_, spender_, id_);\n }\n\n /// @notice approves all NFTs owned by msg.sender to be spent (transferred) by `operator_`\n function setApprovalForAll(address operator_, bool approved_) public virtual {\n isApprovedForAll[msg.sender][operator_] = approved_;\n\n emit ApprovalForAll(msg.sender, operator_, approved_);\n }\n\n /// @notice transfers an NFT with `id_` `from_` address `to_` address without safe check\n function transferFrom(address from_, address to_, uint256 id_) public virtual {\n uint256 tokenConfig_ = _tokenConfig[id_];\n if (from_ != address(uint160(tokenConfig_))) revert FluidVaultError(ErrorTypes.ERC721__InvalidParams);\n\n if (!(msg.sender == from_ || isApprovedForAll[from_][msg.sender] || msg.sender == getApproved[id_]))\n revert FluidVaultError(ErrorTypes.ERC721__Unauthorized);\n\n // call _transfer with vaultId extracted from tokenConfig_\n _transfer(from_, to_, id_, (tokenConfig_ >> 192) & type(uint32).max);\n\n delete getApproved[id_];\n\n emit Transfer(from_, to_, id_);\n }\n\n /// @notice transfers an NFT with `id_` `from_` address `to_` address\n function safeTransferFrom(address from_, address to_, uint256 id_) public virtual {\n transferFrom(from_, to_, id_);\n\n if (\n !(to_.code.length == 0 ||\n ERC721TokenReceiver(to_).onERC721Received(msg.sender, from_, id_, \"\") ==\n ERC721TokenReceiver.onERC721Received.selector)\n ) revert FluidVaultError(ErrorTypes.ERC721__UnsafeRecipient);\n }\n\n /// @notice transfers an NFT with `id_` `from_` address `to_` address, passing `data_` to `onERC721Received` callback\n function safeTransferFrom(address from_, address to_, uint256 id_, bytes calldata data_) public virtual {\n transferFrom(from_, to_, id_);\n\n if (\n !((to_.code.length == 0) ||\n ERC721TokenReceiver(to_).onERC721Received(msg.sender, from_, id_, data_) ==\n ERC721TokenReceiver.onERC721Received.selector)\n ) revert FluidVaultError(ErrorTypes.ERC721__UnsafeRecipient);\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC721Enumerable LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /// @notice Returns a token ID at a given `index_` of all the tokens stored by the contract.\n /// Use along with {totalSupply} to enumerate all tokens.\n function tokenByIndex(uint256 index_) external view returns (uint256) {\n if (index_ >= totalSupply) {\n revert FluidVaultError(ErrorTypes.ERC721__OutOfBoundsIndex);\n }\n return index_ + 1;\n }\n\n /// @notice Returns a token ID owned by `owner_` at a given `index_` of its token list.\n /// Use along with {balanceOf} to enumerate all of `owner_`'s tokens.\n function tokenOfOwnerByIndex(address owner_, uint256 index_) external view returns (uint256) {\n if (index_ >= balanceOf(owner_)) {\n revert FluidVaultError(ErrorTypes.ERC721__OutOfBoundsIndex);\n }\n\n index_ = index_ + 1;\n return (_ownerConfig[owner_][index_ / 8] >> ((index_ % 8) * 32)) & type(uint32).max;\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC165 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function supportsInterface(bytes4 interfaceId_) public view virtual returns (bool) {\n return\n interfaceId_ == 0x01ffc9a7 || // ERC165 Interface ID for ERC165\n interfaceId_ == 0x80ac58cd || // ERC165 Interface ID for ERC721\n interfaceId_ == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata\n interfaceId_ == 0x780e9d63; // ERC165 Interface ID for ERC721Enumberable\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL TRANSFER LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function _transfer(address from_, address to_, uint256 id_, uint256 vaultId_) internal {\n if (to_ == address(0)) {\n revert FluidVaultError(ErrorTypes.ERC721__InvalidOperation);\n } else if (from_ == address(0)) {\n _add(to_, id_, vaultId_);\n } else if (to_ != from_) {\n _remove(from_, id_);\n _add(to_, id_, vaultId_);\n }\n }\n\n function _add(address user_, uint256 id_, uint256 vaultId_) private {\n uint256 ownerConfig_ = _ownerConfig[user_][0];\n unchecked {\n // index starts from `1`\n uint256 balanceOf_ = (ownerConfig_ & type(uint32).max) + 1;\n\n _tokenConfig[id_] = (uint160(user_) | (balanceOf_ << 160) | (vaultId_ << 192));\n\n _ownerConfig[user_][0] = (ownerConfig_ & ~uint256(type(uint32).max)) | (balanceOf_);\n\n uint256 wordIndex_ = (balanceOf_ / 8);\n _ownerConfig[user_][wordIndex_] = _ownerConfig[user_][wordIndex_] | (id_ << ((balanceOf_ % 8) * 32));\n }\n }\n\n function _remove(address user_, uint256 id_) private {\n uint256 temp_ = _tokenConfig[id_];\n\n // fetching `id_` details and deleting it.\n uint256 tokenIndex_ = (temp_ >> 160) & type(uint32).max;\n _tokenConfig[id_] = 0;\n\n // fetching & updating balance\n temp_ = _ownerConfig[user_][0];\n uint256 lastTokenIndex_ = (temp_ & type(uint32).max); // (lastTokenIndex_ = balanceOf)\n _ownerConfig[user_][0] = (temp_ & ~uint256(type(uint32).max)) | (lastTokenIndex_ - 1);\n\n {\n unchecked {\n uint256 lastTokenWordIndex_ = (lastTokenIndex_ / 8);\n uint256 lastTokenBitShift_ = (lastTokenIndex_ % 8) * 32;\n temp_ = _ownerConfig[user_][lastTokenWordIndex_];\n\n // replace `id_` tokenId with `last` tokenId.\n if (lastTokenIndex_ != tokenIndex_) {\n uint256 wordIndex_ = (tokenIndex_ / 8);\n uint256 bitShift_ = (tokenIndex_ % 8) * 32;\n\n // temp_ here is _ownerConfig[user_][lastTokenWordIndex_];\n uint256 lastTokenId_ = uint256((temp_ >> lastTokenBitShift_) & type(uint32).max);\n if (wordIndex_ == lastTokenWordIndex_) {\n // this case, when lastToken and currentToken are in same slot.\n // updating temp_ as we will remove the lastToken from this slot itself\n temp_ = (temp_ & ~(uint256(type(uint32).max) << bitShift_)) | (lastTokenId_ << bitShift_);\n } else {\n _ownerConfig[user_][wordIndex_] =\n (_ownerConfig[user_][wordIndex_] & ~(uint256(type(uint32).max) << bitShift_)) |\n (lastTokenId_ << bitShift_);\n }\n _tokenConfig[lastTokenId_] =\n (_tokenConfig[lastTokenId_] & ~(uint256(type(uint32).max) << 160)) |\n (tokenIndex_ << 160);\n }\n\n // temp_ here is _ownerConfig[user_][lastTokenWordIndex_];\n _ownerConfig[user_][lastTokenWordIndex_] = temp_ & ~(uint256(type(uint32).max) << lastTokenBitShift_);\n }\n }\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL MINT LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function _mint(address to_, uint256 vaultId_) internal virtual returns (uint256 id_) {\n\n unchecked {\n ++totalSupply;\n }\n\n id_ = totalSupply;\n if (id_ >= type(uint32).max || _tokenConfig[id_] != 0) revert FluidVaultError(ErrorTypes.ERC721__InvalidParams);\n\n _transfer(address(0), to_, id_, vaultId_);\n\n emit Transfer(address(0), to_, id_);\n }\n}\n\nabstract contract ERC721TokenReceiver {\n function onERC721Received(address, address, uint256, bytes calldata) external virtual returns (bytes4) {\n return ERC721TokenReceiver.onERC721Received.selector;\n }\n}\n" }, "contracts/protocols/vault/factory/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Owned } from \"solmate/src/auth/Owned.sol\";\nimport { ERC721 } from \"./ERC721/ERC721.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\nimport { StorageRead } from \"../../../libraries/storageRead.sol\";\n\nabstract contract VaultFactoryVariables is Owned, ERC721, StorageRead {\n /// @dev ERC721 tokens name\n string internal constant ERC721_NAME = \"Fluid Vault\";\n /// @dev ERC721 tokens symbol\n string internal constant ERC721_SYMBOL = \"fVLT\";\n\n /*//////////////////////////////////////////////////////////////\n STORAGE VARIABLES\n //////////////////////////////////////////////////////////////*/\n\n // ------------ storage variables from inherited contracts (Owned and ERC721) come before vars here --------\n\n // ----------------------- slot 0 ---------------------------\n // address public owner; // from Owned\n\n // 12 bytes empty\n\n // ----------------------- slot 1 ---------------------------\n // string public name;\n\n // ----------------------- slot 2 ---------------------------\n // string public symbol;\n\n // ----------------------- slot 3 ---------------------------\n // mapping(uint256 => uint256) internal _tokenConfig;\n\n // ----------------------- slot 4 ---------------------------\n // mapping(address => mapping(uint256 => uint256)) internal _ownerConfig;\n\n // ----------------------- slot 5 ---------------------------\n // uint256 public totalSupply;\n\n // ----------------------- slot 6 ---------------------------\n // mapping(uint256 => address) public getApproved;\n\n // ----------------------- slot 7 ---------------------------\n // mapping(address => mapping(address => bool)) public isApprovedForAll;\n\n // ----------------------- slot 8 ---------------------------\n /// @dev deployer can deploy new Vault contract\n /// owner can add/remove deployer.\n /// Owner is deployer by default.\n mapping(address => bool) internal _deployers;\n\n // ----------------------- slot 9 ---------------------------\n /// @dev global auths can update any vault config.\n /// owner can add/remove global auths.\n /// Owner is global auth by default.\n mapping(address => bool) internal _globalAuths;\n\n // ----------------------- slot 10 ---------------------------\n /// @dev vault auths can update specific vault config.\n /// owner can add/remove vault auths.\n /// Owner is vault auth by default.\n /// vault => auth => add/remove\n mapping(address => mapping(address => bool)) internal _vaultAuths;\n\n // ----------------------- slot 11 ---------------------------\n /// @dev total no of vaults deployed by the factory\n /// only addresses that have deployer role or owner can deploy new vault.\n uint256 internal _totalVaults;\n\n // ----------------------- slot 12 ---------------------------\n /// @dev vault deployment logics for deploying vault\n /// These logic contracts hold the deployment logics of specific vaults and are called via .delegatecall inside deployVault().\n /// only addresses that have owner can add/remove new vault deployment logic.\n mapping(address => bool) internal _vaultDeploymentLogics;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n constructor(address owner_) Owned(owner_) ERC721(ERC721_NAME, ERC721_SYMBOL) {}\n}\n\nabstract contract VaultFactoryEvents {\n /// @dev Emitted when a new vault is deployed.\n /// @param vault The address of the newly deployed vault.\n /// @param vaultId The id of the newly deployed vault.\n event VaultDeployed(address indexed vault, uint256 indexed vaultId);\n\n /// @dev Emitted when a new token/position is minted by a vault.\n /// @param vault The address of the vault that minted the token.\n /// @param user The address of the user who received the minted token.\n /// @param tokenId The ID of the newly minted token.\n event NewPositionMinted(address indexed vault, address indexed user, uint256 indexed tokenId);\n\n /// @dev Emitted when the deployer is modified by owner.\n /// @param deployer Address whose deployer status is updated.\n /// @param allowed Indicates whether the address is authorized as a deployer or not.\n event LogSetDeployer(address indexed deployer, bool indexed allowed);\n\n /// @dev Emitted when the globalAuth is modified by owner.\n /// @param globalAuth Address whose globalAuth status is updated.\n /// @param allowed Indicates whether the address is authorized as a deployer or not.\n event LogSetGlobalAuth(address indexed globalAuth, bool indexed allowed);\n\n /// @dev Emitted when the vaultAuth is modified by owner.\n /// @param vaultAuth Address whose vaultAuth status is updated.\n /// @param allowed Indicates whether the address is authorized as a deployer or not.\n /// @param vault Address of the specific vault related to the authorization change.\n event LogSetVaultAuth(address indexed vaultAuth, bool indexed allowed, address indexed vault);\n\n /// @dev Emitted when the vault deployment logic is modified by owner.\n /// @param vaultDeploymentLogic The address of the vault deployment logic contract.\n /// @param allowed Indicates whether the address is authorized as a deployer or not.\n event LogSetVaultDeploymentLogic(address indexed vaultDeploymentLogic, bool indexed allowed);\n}\n\nabstract contract VaultFactoryCore is VaultFactoryVariables, VaultFactoryEvents {\n constructor(address owner_) validAddress(owner_) VaultFactoryVariables(owner_) {}\n\n /// @dev validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidVaultError(ErrorTypes.VaultFactory__InvalidParams);\n }\n _;\n }\n}\n\n/// @dev Implements Vault Factory auth-only callable methods. Owner / auths can set various config values and\n/// can define the allow-listed deployers.\nabstract contract VaultFactoryAuth is VaultFactoryCore {\n /// @notice Sets an address (`deployer_`) as allowed deployer or not.\n /// This function can only be called by the owner.\n /// @param deployer_ The address to be set as deployer.\n /// @param allowed_ A boolean indicating whether the specified address is allowed to deploy vaults.\n function setDeployer(address deployer_, bool allowed_) external onlyOwner validAddress(deployer_) {\n _deployers[deployer_] = allowed_;\n\n emit LogSetDeployer(deployer_, allowed_);\n }\n\n /// @notice Sets an address (`globalAuth_`) as a global authorization or not.\n /// This function can only be called by the owner.\n /// @param globalAuth_ The address to be set as global authorization.\n /// @param allowed_ A boolean indicating whether the specified address is allowed to update any vault config.\n function setGlobalAuth(address globalAuth_, bool allowed_) external onlyOwner validAddress(globalAuth_) {\n _globalAuths[globalAuth_] = allowed_;\n\n emit LogSetGlobalAuth(globalAuth_, allowed_);\n }\n\n /// @notice Sets an address (`vaultAuth_`) as allowed vault authorization or not for a specific vault (`vault_`).\n /// This function can only be called by the owner.\n /// @param vault_ The address of the vault for which the authorization is being set.\n /// @param vaultAuth_ The address to be set as vault authorization.\n /// @param allowed_ A boolean indicating whether the specified address is allowed to update the specific vault config.\n function setVaultAuth(\n address vault_,\n address vaultAuth_,\n bool allowed_\n ) external onlyOwner validAddress(vaultAuth_) {\n _vaultAuths[vault_][vaultAuth_] = allowed_;\n\n emit LogSetVaultAuth(vaultAuth_, allowed_, vault_);\n }\n\n /// @notice Sets an address as allowed vault deployment logic (`deploymentLogic_`) contract or not.\n /// This function can only be called by the owner.\n /// @param deploymentLogic_ The address of the vault deployment logic contract to be set.\n /// @param allowed_ A boolean indicating whether the specified address is allowed to deploy new type of vault.\n function setVaultDeploymentLogic(\n address deploymentLogic_,\n bool allowed_\n ) public onlyOwner validAddress(deploymentLogic_) {\n _vaultDeploymentLogics[deploymentLogic_] = allowed_;\n\n emit LogSetVaultDeploymentLogic(deploymentLogic_, allowed_);\n }\n\n /// @notice Spell allows owner aka governance to do any arbitrary call on factory\n /// @param target_ Address to which the call needs to be delegated\n /// @param data_ Data to execute at the delegated address\n function spell(address target_, bytes memory data_) external onlyOwner returns (bytes memory response_) {\n assembly {\n let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)\n let size := returndatasize()\n\n response_ := mload(0x40)\n mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))\n mstore(response_, size)\n returndatacopy(add(response_, 0x20), 0, size)\n\n switch iszero(succeeded)\n case 1 {\n // throw if delegatecall failed\n returndatacopy(0x00, 0x00, size)\n revert(0x00, size)\n }\n }\n }\n\n /// @notice Checks if the provided address (`deployer_`) is authorized as a deployer.\n /// @param deployer_ The address to be checked for deployer authorization.\n /// @return Returns `true` if the address is a deployer, otherwise `false`.\n function isDeployer(address deployer_) public view returns (bool) {\n return _deployers[deployer_] || owner == deployer_;\n }\n\n /// @notice Checks if the provided address (`globalAuth_`) has global vault authorization privileges.\n /// @param globalAuth_ The address to be checked for global authorization privileges.\n /// @return Returns `true` if the given address has global authorization privileges, otherwise `false`.\n function isGlobalAuth(address globalAuth_) public view returns (bool) {\n return _globalAuths[globalAuth_] || owner == globalAuth_;\n }\n\n /// @notice Checks if the provided address (`vaultAuth_`) has vault authorization privileges for the specified vault (`vault_`).\n /// @param vault_ The address of the vault to check.\n /// @param vaultAuth_ The address to be checked for vault authorization privileges.\n /// @return Returns `true` if the given address has vault authorization privileges for the specified vault, otherwise `false`.\n function isVaultAuth(address vault_, address vaultAuth_) public view returns (bool) {\n return _vaultAuths[vault_][vaultAuth_] || owner == vaultAuth_;\n }\n\n /// @notice Checks if the provided (`vaultDeploymentLogic_`) address has authorization for vault deployment.\n /// @param vaultDeploymentLogic_ The address of the vault deploy logic to check for authorization privileges.\n /// @return Returns `true` if the given address has authorization privileges for vault deployment, otherwise `false`.\n function isVaultDeploymentLogic(address vaultDeploymentLogic_) public view returns (bool) {\n return _vaultDeploymentLogics[vaultDeploymentLogic_];\n }\n}\n\n/// @dev implements VaultFactory deploy vault related methods.\nabstract contract VaultFactoryDeployment is VaultFactoryCore, VaultFactoryAuth {\n /// @dev Deploys a contract using the CREATE opcode with the provided bytecode (`bytecode_`).\n /// This is an internal function, meant to be used within the contract to facilitate the deployment of other contracts.\n /// @param bytecode_ The bytecode of the contract to be deployed.\n /// @return address_ Returns the address of the deployed contract.\n function _deploy(bytes memory bytecode_) internal returns (address address_) {\n if (bytecode_.length == 0) {\n revert FluidVaultError(ErrorTypes.VaultFactory__InvalidOperation);\n }\n /// @solidity memory-safe-assembly\n assembly {\n address_ := create(0, add(bytecode_, 0x20), mload(bytecode_))\n }\n if (address_ == address(0)) {\n revert FluidVaultError(ErrorTypes.VaultFactory__InvalidOperation);\n }\n }\n\n /// @notice Deploys a new vault using the specified deployment logic `vaultDeploymentLogic_` and data `vaultDeploymentData_`.\n /// Only accounts with deployer access or the owner can deploy a new vault.\n /// @param vaultDeploymentLogic_ The address of the vault deployment logic contract.\n /// @param vaultDeploymentData_ The data to be used for vault deployment.\n /// @return vault_ Returns the address of the newly deployed vault.\n function deployVault(\n address vaultDeploymentLogic_,\n bytes calldata vaultDeploymentData_\n ) external returns (address vault_) {\n // Revert if msg.sender doesn't have deployer access or is an owner.\n if (!isDeployer(msg.sender)) revert FluidVaultError(ErrorTypes.VaultFactory__Unauthorized);\n // Revert if vaultDeploymentLogic_ is not whitelisted.\n if (!isVaultDeploymentLogic(vaultDeploymentLogic_))\n revert FluidVaultError(ErrorTypes.VaultFactory__Unauthorized);\n\n // Vault ID for the new vault and also acts as `nonce` for CREATE\n uint256 vaultId_ = ++_totalVaults;\n\n // compute vault address for vault id.\n vault_ = getVaultAddress(vaultId_);\n\n // deploy the vault using vault deployment logic by making .delegatecall\n (bool success_, bytes memory data_) = vaultDeploymentLogic_.delegatecall(vaultDeploymentData_);\n\n if (!(success_ && vault_ == _deploy(abi.decode(data_, (bytes))) && isVault(vault_))) {\n revert FluidVaultError(ErrorTypes.VaultFactory__InvalidVaultAddress);\n }\n\n emit VaultDeployed(vault_, vaultId_);\n }\n\n /// @notice Computes the address of a vault based on its given ID (`vaultId_`).\n /// @param vaultId_ The ID of the vault.\n /// @return vault_ Returns the computed address of the vault.\n function getVaultAddress(uint256 vaultId_) public view returns (address vault_) {\n // @dev based on https://ethereum.stackexchange.com/a/61413\n\n // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment\n // hence, nonce of vault deployment starts with 1.\n bytes memory data;\n if (vaultId_ == 0x00) {\n return address(0);\n } else if (vaultId_ <= 0x7f) {\n data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), address(this), uint8(vaultId_));\n } else if (vaultId_ <= 0xff) {\n data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), address(this), bytes1(0x81), uint8(vaultId_));\n } else if (vaultId_ <= 0xffff) {\n data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), address(this), bytes1(0x82), uint16(vaultId_));\n } else if (vaultId_ <= 0xffffff) {\n data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), address(this), bytes1(0x83), uint24(vaultId_));\n } else {\n data = abi.encodePacked(bytes1(0xda), bytes1(0x94), address(this), bytes1(0x84), uint32(vaultId_));\n }\n\n return address(uint160(uint256(keccak256(data))));\n }\n\n /// @notice Checks if a given address (`vault_`) corresponds to a valid vault.\n /// @param vault_ The vault address to check.\n /// @return Returns `true` if the given address corresponds to a valid vault, otherwise `false`.\n function isVault(address vault_) public view returns (bool) {\n if (vault_.code.length == 0) {\n return false;\n } else {\n // VAULT_ID() function signature is 0x540acabc\n (bool success_, bytes memory data_) = vault_.staticcall(hex\"540acabc\");\n return success_ && vault_ == getVaultAddress(abi.decode(data_, (uint256)));\n }\n }\n\n /// @notice Returns the total number of vaults deployed by the factory.\n /// @return Returns the total number of vaults.\n function totalVaults() external view returns (uint256) {\n return _totalVaults;\n }\n}\n\nabstract contract VaultFactoryERC721 is VaultFactoryCore, VaultFactoryDeployment {\n /// @notice Mints a new ERC721 token for a specific vault (`vaultId_`) to a specified user (`user_`).\n /// Only the corresponding vault is authorized to mint a token.\n /// @param vaultId_ The ID of the vault that's minting the token.\n /// @param user_ The address receiving the minted token.\n /// @return tokenId_ The ID of the newly minted token.\n function mint(uint256 vaultId_, address user_) external returns (uint256 tokenId_) {\n if (msg.sender != getVaultAddress(vaultId_)) revert FluidVaultError(ErrorTypes.VaultFactory__InvalidVault);\n\n // Using _mint() instead of _safeMint() to allow any msg.sender to receive ERC721 without onERC721Received holder.\n tokenId_ = _mint(user_, vaultId_);\n\n emit NewPositionMinted(msg.sender, user_, tokenId_);\n }\n\n /// @notice Returns the URI of the specified token ID (`id_`).\n /// In this implementation, an empty string is returned as no specific URI is defined.\n /// @param id_ The ID of the token to query.\n /// @return An empty string since no specific URI is defined in this implementation.\n function tokenURI(uint256 id_) public view virtual override returns (string memory) {\n return \"\";\n }\n}\n\n/// @title Fluid VaultFactory\n/// @notice creates Fluid vault protocol vaults, which are interacting with Fluid Liquidity to deposit / borrow funds.\n/// Vaults are created at a deterministic address, given an incrementing `vaultId` (see `getVaultAddress()`).\n/// Vaults can only be deployed by allow-listed deployer addresses.\n/// This factory also implements ERC721-Enumerable, the NFTs are used to represent created user positions. Only vaults\n/// can mint new NFTs.\n/// @dev Note the deployed vaults start out with no config at Liquidity contract.\n/// This must be done by Liquidity auths in a separate step, otherwise no deposits will be possible.\n/// This contract is not upgradeable. It supports adding new vault deployment logic contracts for new, future vaults.\ncontract FluidVaultFactory is VaultFactoryCore, VaultFactoryAuth, VaultFactoryDeployment, VaultFactoryERC721 {\n constructor(address owner_) VaultFactoryCore(owner_) {}\n}\n" }, "contracts/protocols/vault/interfaces/iVaultFactory.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC721Enumerable } from \"@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol\";\n\ninterface IFluidVaultFactory is IERC721Enumerable {\n /// @notice Minting an NFT Vault for the user\n function mint(uint256 vaultId_, address user_) external returns (uint256 tokenId_);\n\n /// @notice returns owner of Vault which is also an NFT\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /// @notice Global auth is auth for all vaults\n function isGlobalAuth(address auth_) external view returns (bool);\n\n /// @notice Vault auth is auth for a specific vault\n function isVaultAuth(address vault_, address auth_) external view returns (bool);\n\n /// @notice Total vaults deployed.\n function totalVaults() external view returns (uint256);\n\n /// @notice Compute vaultAddress\n function getVaultAddress(uint256 vaultId) external view returns (address);\n\n /// @notice read uint256 `result_` for a storage `slot_` key\n function readFromStorage(bytes32 slot_) external view returns (uint256 result_);\n}\n" }, "contracts/protocols/vault/interfaces/iVaultT1.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidVaultT1 {\n /// @notice returns the vault id\n function VAULT_ID() external view returns (uint256);\n\n /// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key.\n function readFromStorage(bytes32 slot_) external view returns (uint256 result_);\n\n struct ConstantViews {\n address liquidity;\n address factory;\n address adminImplementation;\n address secondaryImplementation;\n address supplyToken;\n address borrowToken;\n uint8 supplyDecimals;\n uint8 borrowDecimals;\n uint vaultId;\n bytes32 liquiditySupplyExchangePriceSlot;\n bytes32 liquidityBorrowExchangePriceSlot;\n bytes32 liquidityUserSupplySlot;\n bytes32 liquidityUserBorrowSlot;\n }\n\n /// @notice returns all Vault constants\n function constantsView() external view returns (ConstantViews memory constantsView_);\n\n /// @notice fetches the latest user position after a liquidation\n function fetchLatestPosition(\n int256 positionTick_,\n uint256 positionTickId_,\n uint256 positionRawDebt_,\n uint256 tickData_\n )\n external\n view\n returns (\n int256, // tick\n uint256, // raw debt\n uint256, // raw collateral\n uint256, // branchID_\n uint256 // branchData_\n );\n\n /// @notice calculates the updated vault exchange prices\n function updateExchangePrices(\n uint256 vaultVariables2_\n )\n external\n view\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n );\n\n /// @notice calculates the updated vault exchange prices and writes them to storage\n function updateExchangePricesOnStorage()\n external\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n );\n\n /// @notice returns the liquidity contract address\n function LIQUIDITY() external view returns (address);\n\n function operate(\n uint256 nftId_, // if 0 then new position\n int256 newCol_, // if negative then withdraw\n int256 newDebt_, // if negative then payback\n address to_ // address at which the borrow & withdraw amount should go to. If address(0) then it'll go to msg.sender\n )\n external\n payable\n returns (\n uint256, // nftId_\n int256, // final supply amount. if - then withdraw\n int256 // final borrow amount. if - then payback\n );\n \n function liquidate(\n uint256 debtAmt_,\n uint256 colPerUnitDebt_, // min collateral needed per unit of debt in 1e18\n address to_,\n bool absorb_\n ) external payable returns (uint actualDebtAmt_, uint actualColAmt_);\n\n function absorb() external;\n\n function rebalance() external payable returns (int supplyAmt_, int borrowAmt_);\n\n error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated);\n}\n" }, "contracts/protocols/vault/rewards/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Events {\n /// @notice Emitted when magnifier is updated\n event LogUpdateMagnifier(address indexed vault, uint256 newMagnifier);\n\n /// @notice Emitted when rewards are started\n event LogRewardsStarted(uint256 startTime, uint256 endTime);\n}\n" }, "contracts/protocols/vault/rewards/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidVaultT1Admin } from \"../vaultT1/adminModule/main.sol\";\nimport { IFluidVaultT1 } from \"../interfaces/iVaultT1.sol\";\nimport { IFluidReserveContract } from \"../../../reserve/interfaces/iReserveContract.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { Events } from \"./events.sol\";\nimport { Variables } from \"./variables.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\n\n/// @title VaultRewards\n/// @notice This contract is designed to adjust the supply rate magnifier for a vault based on the current collateral supply & supply rate.\n/// The adjustment aims to dynamically scale the rewards given to lenders as the TVL in the vault changes.\n///\n/// The magnifier is adjusted based on a regular most used reward type where rewardRate = totalRewardsAnnually / totalSupply.\n/// Reward rate is applied by adjusting the supply magnifier on vault.\n/// Adjustments are made via the rebalance function, which is restricted to be called by designated rebalancers only.\ncontract FluidVaultRewards is Variables, Events, Error {\n /// @dev Validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidVaultError(ErrorTypes.VaultRewards__AddressZero);\n }\n _;\n }\n\n /// @dev Validates that an address is a rebalancer (taken from reserve contract)\n modifier onlyRebalancer() {\n if (!RESERVE_CONTRACT.isRebalancer(msg.sender)) {\n revert FluidVaultError(ErrorTypes.VaultRewards__Unauthorized);\n }\n _;\n }\n\n /// @notice Constructs the FluidVaultRewards contract.\n /// @param reserveContract_ The address of the reserve contract where rebalancers are defined.\n /// @param vault_ The vault to which this contract will apply new magnifier parameter.\n /// @param liquidity_ Fluid liquidity address\n /// @param rewardsAmt_ Amounts of rewards to distribute\n /// @param duration_ rewards duration\n /// @param initiator_ address that can start rewards with `start()`\n /// @param collateralToken_ vault collateral token address\n constructor(\n IFluidReserveContract reserveContract_,\n IFluidVaultT1 vault_,\n IFluidLiquidity liquidity_,\n uint256 rewardsAmt_,\n uint256 duration_,\n address initiator_,\n address collateralToken_\n ) validAddress(address(reserveContract_)) validAddress(address(liquidity_)) validAddress(address(vault_)) validAddress(initiator_) validAddress(address(collateralToken_)){\n if (rewardsAmt_ == 0 || duration_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultRewards__InvalidParams);\n }\n RESERVE_CONTRACT = reserveContract_;\n VAULT = vault_;\n REWARDS_AMOUNT = rewardsAmt_;\n REWARDS_AMOUNT_PER_YEAR = rewardsAmt_ * SECONDS_PER_YEAR / duration_;\n DURATION = duration_;\n INITIATOR = initiator_;\n LIQUIDITY = liquidity_;\n VAULT_COLLATERAL_TOKEN = collateralToken_;\n\n LIQUIDITY_TOTAL_AMOUNTS_COLLATERAL_TOKEN_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n collateralToken_\n );\n LIQUIDITY_EXCHANGE_PRICE_COLLATERAL_TOKEN_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n collateralToken_\n );\n }\n\n /// @notice Rebalances the supply rate magnifier based on the current collateral supply.\n /// Can only be called by an authorized rebalancer.\n function rebalance() external onlyRebalancer {\n (uint256 newMagnifier_, bool ended_) = calculateMagnifier();\n if (ended_) {\n ended = true;\n }\n if (newMagnifier_ == currentMagnifier()) {\n revert FluidVaultError(ErrorTypes.VaultRewards__NewMagnifierSameAsOldMagnifier);\n }\n\n FluidVaultT1Admin(address(VAULT)).updateSupplyRateMagnifier(newMagnifier_);\n emit LogUpdateMagnifier(address(VAULT), newMagnifier_);\n }\n\n /// @notice Calculates the new supply rate magnifier based on the current collateral supply (`vaultTVL()`).\n /// @return magnifier_ The calculated magnifier value.\n function calculateMagnifier() public view returns (uint256 magnifier_, bool ended_) {\n uint256 currentTVL_ = vaultTVL();\n uint256 startTime_ = uint256(startTime);\n uint256 endTime_ = uint256(endTime);\n\n if (startTime_ == 0 || endTime_ == 0 || ended) {\n revert FluidVaultError(ErrorTypes.VaultRewards__RewardsNotStartedOrEnded);\n }\n\n if (block.timestamp > endTime_) {\n return (FOUR_DECIMALS, true);\n }\n\n uint supplyRate_ = getSupplyRate();\n uint rewardsRate_ = (REWARDS_AMOUNT_PER_YEAR * FOUR_DECIMALS) / currentTVL_;\n\n magnifier_ = FOUR_DECIMALS + (supplyRate_ == 0 ? rewardsRate_ : ((rewardsRate_ * FOUR_DECIMALS) / supplyRate_));\n if (magnifier_ > X16) {\n magnifier_ = X16;\n }\n }\n\n /// @notice returns the currently configured supply magnifier at the `VAULT`.\n function currentMagnifier() public view returns (uint256) {\n // read supply rate magnifier from Vault `vaultVariables2` located in storage slot 1, first 16 bits\n return VAULT.readFromStorage(bytes32(uint256(1))) & X16;\n }\n\n /// @notice returns the current total value locked as collateral (TVL) in the `VAULT`.\n function vaultTVL() public view returns (uint256 tvl_) {\n // read total supply raw in vault from storage slot 0 `vaultVariables`, 64 bits 82-145\n tvl_ = (VAULT.readFromStorage(bytes32(0)) >> 82) & 0xFFFFFFFFFFFFFFFF;\n\n // Converting bignumber into normal number\n tvl_ = (tvl_ >> 8) << (tvl_ & 0xFF);\n\n // get updated supply exchange price, which takes slot 1 `vaultVariables2` as input param\n (, , uint256 vaultSupplyExPrice_, ) = VAULT.updateExchangePrices(VAULT.readFromStorage(bytes32(uint256(1))));\n\n // converting raw total supply into normal amount\n tvl_ = (tvl_ * vaultSupplyExPrice_) / 1e12;\n }\n\n function getSupplyRate() public view returns (uint supplyRate_) {\n uint256 exchangePriceAndConfig_ = LIQUIDITY.readFromStorage(LIQUIDITY_EXCHANGE_PRICE_COLLATERAL_TOKEN_SLOT);\n uint256 totalAmounts_ = LIQUIDITY.readFromStorage(LIQUIDITY_TOTAL_AMOUNTS_COLLATERAL_TOKEN_SLOT);\n\n uint borrowRate_ = exchangePriceAndConfig_ & X16;\n uint fee_ = (exchangePriceAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14;\n uint supplyExchangePrice_ = ((exchangePriceAndConfig_ >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64);\n uint borrowExchangePrice_ = ((exchangePriceAndConfig_ >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64);\n\n // Extract supply raw interest\n uint256 supplyWithInterest_ = totalAmounts_ & X64;\n supplyWithInterest_ =\n (supplyWithInterest_ >> DEFAULT_EXPONENT_SIZE) <<\n (supplyWithInterest_ & DEFAULT_EXPONENT_MASK);\n\n // Extract borrow raw interest\n uint256 borrowWithInterest_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) &\n X64;\n borrowWithInterest_ =\n (borrowWithInterest_ >> DEFAULT_EXPONENT_SIZE) <<\n (borrowWithInterest_ & DEFAULT_EXPONENT_MASK);\n\n if (supplyWithInterest_ > 0) {\n // use old exchange prices for supply rate to be at same level as borrow rate from storage.\n // Note the rate here can be a tiny bit with higher precision because we use borrowWithInterest_ / supplyWithInterest_\n // which has higher precision than the utilization used from storage in LiquidityCalcs\n supplyWithInterest_ = (supplyWithInterest_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION; // normalized from raw\n borrowWithInterest_ = (borrowWithInterest_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION; // normalized from raw\n\n supplyRate_ =\n (borrowRate_ * (FOUR_DECIMALS - fee_) * borrowWithInterest_) /\n (supplyWithInterest_ * FOUR_DECIMALS);\n }\n }\n\n function start() external {\n if (msg.sender != INITIATOR) {\n revert FluidVaultError(ErrorTypes.VaultRewards__NotTheInitiator);\n }\n if (startTime > 0 || endTime > 0) {\n revert FluidVaultError(ErrorTypes.VaultRewards__AlreadyStarted);\n }\n startTime = uint96(block.timestamp);\n endTime = uint96(block.timestamp + DURATION);\n\n emit LogRewardsStarted(startTime, endTime);\n }\n}\n" }, "contracts/protocols/vault/rewards/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidReserveContract } from \"../../../reserve/interfaces/iReserveContract.sol\";\nimport { IFluidVaultT1 } from \"../interfaces/iVaultT1.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\n\nabstract contract Constants {\n IFluidLiquidity public immutable LIQUIDITY;\n IFluidReserveContract public immutable RESERVE_CONTRACT;\n IFluidVaultT1 public immutable VAULT;\n uint256 public immutable REWARDS_AMOUNT;\n uint256 public immutable REWARDS_AMOUNT_PER_YEAR;\n uint256 public immutable DURATION;\n address public immutable INITIATOR;\n address public immutable VAULT_COLLATERAL_TOKEN;\n\n bytes32 internal immutable LIQUIDITY_TOTAL_AMOUNTS_COLLATERAL_TOKEN_SLOT;\n bytes32 internal immutable LIQUIDITY_EXCHANGE_PRICE_COLLATERAL_TOKEN_SLOT;\n uint256 internal constant FOUR_DECIMALS = 10000;\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xff;\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n}\n\nabstract contract Variables is Constants {\n bool public ended; // when rewards are ended\n uint96 public startTime;\n uint96 public endTime;\n}\n" }, "contracts/protocols/vault/vaultT1/adminModule/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Events {\n /// @notice emitted when the supply rate magnifier config is updated\n event LogUpdateSupplyRateMagnifier(uint supplyRateMagnifier_);\n\n /// @notice emitted when the borrow rate magnifier config is updated\n event LogUpdateBorrowRateMagnifier(uint borrowRateMagnifier_);\n\n /// @notice emitted when the collateral factor config is updated\n event LogUpdateCollateralFactor(uint collateralFactor_);\n\n /// @notice emitted when the liquidation threshold config is updated\n event LogUpdateLiquidationThreshold(uint liquidationThreshold_);\n\n /// @notice emitted when the liquidation max limit config is updated\n event LogUpdateLiquidationMaxLimit(uint liquidationMaxLimit_);\n\n /// @notice emitted when the withdrawal gap config is updated\n event LogUpdateWithdrawGap(uint withdrawGap_);\n\n /// @notice emitted when the liquidation penalty config is updated\n event LogUpdateLiquidationPenalty(uint liquidationPenalty_);\n\n /// @notice emitted when the borrow fee config is updated\n event LogUpdateBorrowFee(uint borrowFee_);\n\n /// @notice emitted when the core setting configs are updated\n event LogUpdateCoreSettings(\n uint supplyRateMagnifier_,\n uint borrowRateMagnifier_,\n uint collateralFactor_,\n uint liquidationThreshold_,\n uint liquidationMaxLimit_,\n uint withdrawGap_,\n uint liquidationPenalty_,\n uint borrowFee_\n );\n\n /// @notice emitted when the oracle is updated\n event LogUpdateOracle(address indexed newOracle_);\n\n /// @notice emitted when the allowed rebalancer is updated\n event LogUpdateRebalancer(address indexed newRebalancer_);\n\n /// @notice emitted when funds are rescued\n event LogRescueFunds(address indexed token_);\n\n /// @notice emitted when dust debt is absorbed for `nftIds_`\n event LogAbsorbDustDebt(uint256[] nftIds_, uint256 absorbedDustDebt_);\n}\n" }, "contracts/protocols/vault/vaultT1/adminModule/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Variables } from \"../common/variables.sol\";\nimport { Events } from \"./events.sol\";\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { Error } from \"../../error.sol\";\nimport { IFluidVaultT1 } from \"../../interfaces/iVaultT1.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\n\n/// @notice Fluid Vault protocol Admin Module contract.\n/// Implements admin related methods to set configs such as liquidation params, rates\n/// oracle address etc.\n/// Methods are limited to be called via delegateCall only. Vault CoreModule (\"VaultT1\" contract)\n/// is expected to call the methods implemented here after checking the msg.sender is authorized.\n/// All methods update the exchange prices in storage before changing configs.\ncontract FluidVaultT1Admin is Variables, Events, Error {\n uint private constant X8 = 0xff;\n uint private constant X10 = 0x3ff;\n uint private constant X16 = 0xffff;\n uint private constant X19 = 0x7ffff;\n uint private constant X24 = 0xffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n uint private constant X96 = 0xffffffffffffffffffffffff;\n address private constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n address private immutable addressThis;\n\n constructor() {\n addressThis = address(this);\n }\n\n modifier _verifyCaller() {\n if (address(this) == addressThis) {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__OnlyDelegateCallAllowed);\n }\n _;\n }\n\n /// @dev updates exchange price on storage, called on all admin methods in combination with _verifyCaller modifier so\n /// only called by authorized delegatecall\n modifier _updateExchangePrice() {\n IFluidVaultT1(address(this)).updateExchangePricesOnStorage();\n _;\n }\n\n function _checkLiquidationMaxLimitAndPenalty(uint liquidationMaxLimit_, uint liquidationPenalty_) private pure {\n // liquidation max limit with penalty should not go above 99.7%\n // As liquidation with penalty can happen from liquidation Threshold to max limit\n // If it goes above 100% than that means liquidator is getting more collateral than user's available\n if ((liquidationMaxLimit_ + liquidationPenalty_) > 9970) {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n }\n }\n\n /// @notice updates the supply rate magnifier to `supplyRateMagnifier_`. Input in 1e2 (1% = 100, 100% = 10_000).\n function updateSupplyRateMagnifier(uint supplyRateMagnifier_) public _updateExchangePrice _verifyCaller {\n emit LogUpdateSupplyRateMagnifier(supplyRateMagnifier_);\n\n if (supplyRateMagnifier_ > X16) revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n\n vaultVariables2 =\n (vaultVariables2 & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000) |\n supplyRateMagnifier_;\n }\n\n /// @notice updates the borrow rate magnifier to `borrowRateMagnifier_`. Input in 1e2 (1% = 100, 100% = 10_000).\n function updateBorrowRateMagnifier(uint borrowRateMagnifier_) public _updateExchangePrice _verifyCaller {\n emit LogUpdateBorrowRateMagnifier(borrowRateMagnifier_);\n\n if (borrowRateMagnifier_ > X16) revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n\n vaultVariables2 =\n (vaultVariables2 & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff) |\n (borrowRateMagnifier_ << 16);\n }\n\n /// @notice updates the collateral factor to `collateralFactor_`. Input in 1e2 (1% = 100, 100% = 10_000).\n function updateCollateralFactor(uint collateralFactor_) public _updateExchangePrice _verifyCaller {\n emit LogUpdateCollateralFactor(collateralFactor_);\n\n uint vaultVariables2_ = vaultVariables2;\n uint liquidationThreshold_ = ((vaultVariables2_ >> 42) & X10);\n\n collateralFactor_ = collateralFactor_ / 10;\n\n if (collateralFactor_ >= liquidationThreshold_)\n revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n\n vaultVariables2 =\n (vaultVariables2_ & 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffc00ffffffff) |\n (collateralFactor_ << 32);\n }\n\n /// @notice updates the liquidation threshold to `liquidationThreshold_`. Input in 1e2 (1% = 100, 100% = 10_000).\n function updateLiquidationThreshold(uint liquidationThreshold_) public _updateExchangePrice _verifyCaller {\n emit LogUpdateLiquidationThreshold(liquidationThreshold_);\n\n uint vaultVariables2_ = vaultVariables2;\n uint collateralFactor_ = ((vaultVariables2_ >> 32) & X10);\n uint liquidationMaxLimit_ = ((vaultVariables2_ >> 52) & X10);\n\n liquidationThreshold_ = liquidationThreshold_ / 10;\n\n if ((collateralFactor_ >= liquidationThreshold_) || (liquidationThreshold_ >= liquidationMaxLimit_))\n revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n\n vaultVariables2 =\n (vaultVariables2_ & 0xfffffffffffffffffffffffffffffffffffffffffffffffffff003ffffffffff) |\n (liquidationThreshold_ << 42);\n }\n\n /// @notice updates the liquidation max limit to `liquidationMaxLimit_`. Input in 1e2 (1% = 100, 100% = 10_000).\n function updateLiquidationMaxLimit(uint liquidationMaxLimit_) public _updateExchangePrice _verifyCaller {\n emit LogUpdateLiquidationMaxLimit(liquidationMaxLimit_);\n\n uint vaultVariables2_ = vaultVariables2;\n uint liquidationThreshold_ = ((vaultVariables2_ >> 42) & X10);\n uint liquidationPenalty_ = ((vaultVariables2_ >> 72) & X10);\n\n // both are in 1e2 decimals (1e2 = 1%)\n _checkLiquidationMaxLimitAndPenalty(liquidationMaxLimit_, liquidationPenalty_);\n\n liquidationMaxLimit_ = liquidationMaxLimit_ / 10;\n\n if (liquidationThreshold_ >= liquidationMaxLimit_)\n revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n\n vaultVariables2 =\n (vaultVariables2_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffc00fffffffffffff) |\n (liquidationMaxLimit_ << 52);\n }\n\n /// @notice updates the withdrawal gap to `withdrawGap_`. Input in 1e2 (1% = 100, 100% = 10_000).\n function updateWithdrawGap(uint withdrawGap_) public _updateExchangePrice _verifyCaller {\n emit LogUpdateWithdrawGap(withdrawGap_);\n\n withdrawGap_ = withdrawGap_ / 10;\n\n // withdrawGap must not be > 100%\n if (withdrawGap_ > 1000) revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n\n vaultVariables2 =\n (vaultVariables2 & 0xffffffffffffffffffffffffffffffffffffffffffffff003fffffffffffffff) |\n (withdrawGap_ << 62);\n }\n\n /// @notice updates the liquidation penalty to `liquidationPenalty_`. Input in 1e2 (1% = 100, 100% = 10_000).\n function updateLiquidationPenalty(uint liquidationPenalty_) public _updateExchangePrice _verifyCaller {\n emit LogUpdateLiquidationPenalty(liquidationPenalty_);\n\n uint vaultVariables2_ = vaultVariables2;\n uint liquidationMaxLimit_ = ((vaultVariables2_ >> 52) & X10);\n\n // Converting liquidationMaxLimit_ in 1e2 decimals (1e2 = 1%)\n _checkLiquidationMaxLimitAndPenalty((liquidationMaxLimit_ * 10), liquidationPenalty_);\n\n if (liquidationPenalty_ > X10) revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n\n vaultVariables2 =\n (vaultVariables2_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00ffffffffffffffffff) |\n (liquidationPenalty_ << 72);\n }\n\n /// @notice updates the borrow fee to `borrowFee_`. Input in 1e2 (1% = 100, 100% = 10_000).\n function updateBorrowFee(uint borrowFee_) public _updateExchangePrice _verifyCaller {\n emit LogUpdateBorrowFee(borrowFee_);\n\n if (borrowFee_ > X10) revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n\n vaultVariables2 =\n (vaultVariables2 & 0xfffffffffffffffffffffffffffffffffffffffff003ffffffffffffffffffff) |\n (borrowFee_ << 82);\n }\n\n /// @notice updates the all Vault core settings according to input params.\n /// All input values are expected in 1e2 (1% = 100, 100% = 10_000).\n function updateCoreSettings(\n uint256 supplyRateMagnifier_,\n uint256 borrowRateMagnifier_,\n uint256 collateralFactor_,\n uint256 liquidationThreshold_,\n uint256 liquidationMaxLimit_,\n uint256 withdrawGap_,\n uint256 liquidationPenalty_,\n uint256 borrowFee_\n ) public _updateExchangePrice _verifyCaller {\n // emitting the event at the start as then we are updating numbers to store in a more optimized way\n emit LogUpdateCoreSettings(\n supplyRateMagnifier_,\n borrowRateMagnifier_,\n collateralFactor_,\n liquidationThreshold_,\n liquidationMaxLimit_,\n withdrawGap_,\n liquidationPenalty_,\n borrowFee_\n );\n\n _checkLiquidationMaxLimitAndPenalty(liquidationMaxLimit_, liquidationPenalty_);\n\n collateralFactor_ = collateralFactor_ / 10;\n liquidationThreshold_ = liquidationThreshold_ / 10;\n liquidationMaxLimit_ = liquidationMaxLimit_ / 10;\n withdrawGap_ = withdrawGap_ / 10;\n\n if (\n (supplyRateMagnifier_ > X16) ||\n (borrowRateMagnifier_ > X16) ||\n (collateralFactor_ >= liquidationThreshold_) ||\n (liquidationThreshold_ >= liquidationMaxLimit_) ||\n (withdrawGap_ > X10) ||\n (liquidationPenalty_ > X10) ||\n (borrowFee_ > X10)\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__ValueAboveLimit);\n }\n\n vaultVariables2 =\n (vaultVariables2 & 0xfffffffffffffffffffffffffffffffffffffffff00000000000000000000000) |\n supplyRateMagnifier_ |\n (borrowRateMagnifier_ << 16) |\n (collateralFactor_ << 32) |\n (liquidationThreshold_ << 42) |\n (liquidationMaxLimit_ << 52) |\n (withdrawGap_ << 62) |\n (liquidationPenalty_ << 72) |\n (borrowFee_ << 82);\n }\n\n /// @notice updates the Vault oracle to `newOracle_`. Must implement the FluidOracle interface.\n function updateOracle(address newOracle_) public _updateExchangePrice _verifyCaller {\n if (newOracle_ == address(0)) revert FluidVaultError(ErrorTypes.VaultT1Admin__AddressZeroNotAllowed);\n\n // Removing current oracle by masking only first 96 bits then inserting new oracle as bits\n vaultVariables2 = (vaultVariables2 & X96) | (uint256(uint160(newOracle_)) << 96);\n\n emit LogUpdateOracle(newOracle_);\n }\n\n /// @notice updates the allowed rebalancer to `newRebalancer_`.\n function updateRebalancer(address newRebalancer_) public _updateExchangePrice _verifyCaller {\n if (newRebalancer_ == address(0)) revert FluidVaultError(ErrorTypes.VaultT1Admin__AddressZeroNotAllowed);\n\n rebalancer = newRebalancer_;\n\n emit LogUpdateRebalancer(newRebalancer_);\n }\n\n /// @notice sends any potentially stuck funds to Liquidity contract.\n /// @dev this contract never holds any funds as all operations send / receive funds from user <-> Liquidity.\n function rescueFunds(address token_) external _verifyCaller {\n if (token_ == NATIVE_TOKEN) {\n Address.sendValue(payable(IFluidVaultT1(address(this)).LIQUIDITY()), address(this).balance);\n } else {\n SafeERC20.safeTransfer(\n IERC20(token_),\n IFluidVaultT1(address(this)).LIQUIDITY(),\n IERC20(token_).balanceOf(address(this))\n );\n }\n\n emit LogRescueFunds(token_);\n }\n\n /// @notice absorbs accumulated dust debt\n /// @dev in decades if a lot of positions are 100% liquidated (aka absorbed) then dust debt can mount up\n /// which is basically sort of an extra revenue for the protocol.\n //\n // this function might never come in use that's why adding it in admin module\n function absorbDustDebt(uint[] memory nftIds_) public _verifyCaller {\n uint256 vaultVariables_ = vaultVariables;\n // re-entrancy check\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n uint nftId_;\n uint posData_;\n int posTick_;\n uint tickId_;\n uint posCol_;\n uint posDebt_;\n uint posDustDebt_;\n uint tickData_;\n\n uint absorbedDustDebt_ = absorbedDustDebt;\n\n for (uint i = 0; i < nftIds_.length; ) {\n nftId_ = nftIds_[i];\n if (nftId_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__NftIdShouldBeNonZero);\n }\n\n // user's position data\n posData_ = positionData[nftId_];\n\n if (posData_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__NftNotOfThisVault);\n }\n\n posCol_ = (posData_ >> 45) & X64;\n // Converting big number into normal number\n posCol_ = (posCol_ >> 8) << (posCol_ & X8);\n\n posDustDebt_ = (posData_ >> 109) & X64;\n // Converting big number into normal number\n posDustDebt_ = (posDustDebt_ >> 8) << (posDustDebt_ & X8);\n\n if (posDustDebt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__DustDebtIsZero);\n }\n\n // borrow position (has collateral & debt)\n posTick_ = posData_ & 2 == 2 ? int((posData_ >> 2) & X19) : -int((posData_ >> 2) & X19);\n tickId_ = (posData_ >> 21) & X24;\n\n posDebt_ = (TickMath.getRatioAtTick(int24(posTick_)) * posCol_) >> 96;\n\n // Tick data from user's tick\n tickData_ = tickData[posTick_];\n\n // Checking if tick is liquidated OR if the total IDs of tick is greater than user's tick ID\n if (((tickData_ & 1) == 1) || (((tickData_ >> 1) & X24) > tickId_)) {\n // User got liquidated\n (, posDebt_, , , ) = IFluidVaultT1(address(this)).fetchLatestPosition(\n posTick_,\n tickId_,\n posDebt_,\n tickData_\n );\n if (posDebt_ > 0) {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__FinalDebtShouldBeZero);\n }\n // absorbing user's debt as it's 100% or almost 100% liquidated\n absorbedDustDebt_ = absorbedDustDebt_ + posDustDebt_;\n // making position as supply only\n positionData[nftId_] = 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__NftNotLiquidated);\n }\n\n unchecked {\n i++;\n }\n }\n\n if (absorbedDustDebt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1Admin__AbsorbedDustDebtIsZero);\n }\n\n uint totalBorrow_ = (vaultVariables_ >> 146) & X64;\n // Converting big number into normal number\n totalBorrow_ = (totalBorrow_ >> 8) << (totalBorrow_ & X8);\n // note: by default dust debt is not added into total borrow but on 100% liquidation (aka absorb) dust debt equivalent\n // is removed from total borrow so adding it back again here\n totalBorrow_ = totalBorrow_ + absorbedDustDebt_;\n totalBorrow_ = BigMathMinified.toBigNumber(totalBorrow_, 56, 8, BigMathMinified.ROUND_UP);\n\n // adding absorbed dust debt to total borrow so it will get included in the next rebalancing.\n // there is some fuzziness here as when the position got fully liquidated (aka absorbed) the exchange price was different\n // than what it'll be now. The fuzziness which will be extremely small so we can ignore it\n // updating on storage\n vaultVariables =\n (vaultVariables_ & 0xfffffffffffc0000000000000003ffffffffffffffffffffffffffffffffffff) |\n (totalBorrow_ << 146);\n\n // updating on storage\n absorbedDustDebt = 0;\n\n emit LogAbsorbDustDebt(nftIds_, absorbedDustDebt_);\n }\n}\n" }, "contracts/protocols/vault/vaultT1/common/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Variables {\n /***********************************|\n | Storage Variables |\n |__________________________________*/\n\n /// note: in all variables. For tick >= 0 are represented with bit as 1, tick < 0 are represented with bit as 0\n /// note: read all the variables through storageRead.sol\n\n /// note: vaultVariables contains vault variables which need regular updates through transactions\n /// First 1 bit => 0 => re-entrancy. If 0 then allow transaction to go, else throw.\n /// Next 1 bit => 1 => Is the current active branch liquidated? If true then check the branch's minima tick before creating a new position\n /// If the new tick is greater than minima tick then initialize a new branch, make that as current branch & do proper linking\n /// Next 1 bit => 2 => sign of topmost tick (0 -> negative; 1 -> positive)\n /// Next 19 bits => 3-21 => absolute value of topmost tick\n /// Next 30 bits => 22-51 => current branch ID\n /// Next 30 bits => 52-81 => total branch ID\n /// Next 64 bits => 82-145 => Total supply\n /// Next 64 bits => 146-209 => Total borrow\n /// Next 32 bits => 210-241 => Total positions\n uint256 internal vaultVariables;\n\n /// note: vaultVariables2 contains variables which do not update on every transaction. So mainly admin/auth set amount\n /// First 16 bits => 0-15 => supply rate magnifier; 10000 = 1x (Here 16 bits should be more than enough)\n /// Next 16 bits => 16-31 => borrow rate magnifier; 10000 = 1x (Here 16 bits should be more than enough)\n /// Next 10 bits => 32-41 => collateral factor. 800 = 0.8 = 80% (max precision of 0.1%)\n /// Next 10 bits => 42-51 => liquidation Threshold. 900 = 0.9 = 90% (max precision of 0.1%)\n /// Next 10 bits => 52-61 => liquidation Max Limit. 950 = 0.95 = 95% (max precision of 0.1%) (above this 100% liquidation can happen)\n /// Next 10 bits => 62-71 => withdraw gap. 100 = 0.1 = 10%. (max precision of 0.1%) (max 7 bits can also suffice for the requirement here of 0.1% to 10%). Needed to save some limits on withdrawals so liquidate can work seamlessly.\n /// Next 10 bits => 72-81 => liquidation penalty. 100 = 0.01 = 1%. (max precision of 0.01%) (max liquidation penantly can be 10.23%). Applies when tick is in between liquidation Threshold & liquidation Max Limit.\n /// Next 10 bits => 82-91 => borrow fee. 100 = 0.01 = 1%. (max precision of 0.01%) (max borrow fee can be 10.23%). Fees on borrow.\n /// Next 4 bits => 92-95 => empty\n /// Next 160 bits => 96-255 => Oracle address\n uint256 internal vaultVariables2;\n\n /// note: stores absorbed liquidity\n /// First 128 bits raw debt amount\n /// last 128 bits raw col amount\n uint256 internal absorbedLiquidity;\n\n /// position index => position data uint\n /// if the entire variable is 0 (meaning not initialized) at the start that means no position at all\n /// First 1 bit => 0 => position type (0 => borrow position; 1 => supply position)\n /// Next 1 bit => 1 => sign of user's tick (0 => negative; 1 => positive)\n /// Next 19 bits => 2-20 => absolute value of user's tick\n /// Next 24 bits => 21-44 => user's tick's id\n /// Below we are storing user's collateral & not debt, because the position can also be only collateral with no tick but it can never be only debt\n /// Next 64 bits => 45-108 => user's supply amount. Debt will be calculated through supply & ratio.\n /// Next 64 bits => 109-172 => user's dust debt amount. User's net debt = total debt - dust amount. Total debt is calculated through supply & ratio\n /// User won't pay any extra interest on dust debt & hence we will not show it as a debt on UI. For user's there's no dust.\n mapping(uint256 => uint256) internal positionData;\n\n /// Tick has debt only keeps data of non liquidated positions. liquidated tick's data stays in branch itself\n /// tick parent => uint (represents bool for 256 children)\n /// parent of (i)th tick:-\n /// if (i>=0) (i / 256);\n /// else ((i + 1) / 256) - 1\n /// first bit of the variable is the smallest tick & last bit is the biggest tick of that slot\n mapping(int256 => uint256) internal tickHasDebt;\n\n /// mapping tickId => tickData\n /// Tick related data. Total debt & other things\n /// First bit => 0 => If 1 then liquidated else not liquidated\n /// Next 24 bits => 1-24 => Total IDs. ID should start from 1.\n /// If not liquidated:\n /// Next 64 bits => 25-88 => raw debt\n /// If liquidated\n /// The below 3 things are of last ID. This is to be updated when user creates a new position\n /// Next 1 bit => 25 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated (100% liquidated)\n /// Next 30 bits => 26-55 => branch ID where this tick got liquidated\n /// Next 50 bits => 56-105 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n mapping(int256 => uint256) internal tickData;\n\n /// tick id => previous tick id liquidation data. ID starts from 1\n /// One tick ID contains 3 IDs of 80 bits in it, holding liquidation data of previously active but liquidated ticks\n /// 81 bits data below\n /// #### First 85 bits ####\n /// 1st bit => 0 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 1-30 => branch ID where this tick got liquidated\n /// Next 50 bits => 31-80 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n /// #### Second 85 bits ####\n /// 85th bit => 85 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 86-115 => branch ID where this tick got liquidated\n /// Next 50 bits => 116-165 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n /// #### Third 85 bits ####\n /// 170th bit => 170 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 171-200 => branch ID where this tick got liquidated\n /// Next 50 bits => 201-250 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n mapping(int256 => mapping(uint256 => uint256)) internal tickId;\n\n /// mapping branchId => branchData\n /// First 2 bits => 0-1 => if 0 then not liquidated, if 1 then liquidated, if 2 then merged, if 3 then closed\n /// merged means the branch is merged into it's base branch\n /// closed means all the users are 100% liquidated\n /// Next 1 bit => 2 => minima tick sign of this branch. Will only be there if any liquidation happened.\n /// Next 19 bits => 3-21 => minima tick of this branch. Will only be there if any liquidation happened.\n /// Next 30 bits => 22-51 => Partials of minima tick of branch this is connected to. 0 if master branch.\n /// Next 64 bits => 52-115 Debt liquidity at this branch. Similar to last's top tick data. Remaining debt will move here from tickData after first liquidation\n /// If not merged\n /// Next 50 bits => 116-165 => Debt factor or of this branch. (35 bits coefficient | 15 bits expansion)\n /// If merged\n /// Next 50 bits => 116-165 => Connection/adjustment debt factor of this branch with the next branch.\n /// If closed\n /// Next 50 bits => 116-165 => Debt factor as 0. As all the user's positions are now fully gone\n /// following values are present always again (merged / not merged / closed)\n /// Next 30 bits => 166-195 => Branch's ID with which this branch is connected. If 0 then that means this is the master branch\n /// Next 1 bit => 196 => sign of minima tick of branch this is connected to. 0 if master branch.\n /// Next 19 bits => 197-215 => minima tick of branch this is connected to. 0 if master branch.\n mapping(uint256 => uint256) internal branchData;\n\n /// Exchange prices are in 1e12\n /// First 64 bits => 0-63 => Liquidity's collateral token supply exchange price\n /// First 64 bits => 64-127 => Liquidity's debt token borrow exchange price\n /// First 64 bits => 128-191 => Vault's collateral token supply exchange price\n /// First 64 bits => 192-255 => Vault's debt token borrow exchange price\n uint256 internal rates;\n\n /// address of rebalancer\n address internal rebalancer;\n\n uint256 internal absorbedDustDebt;\n}\n" }, "contracts/protocols/vault/vaultT1/coreModule/constantVariables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidVaultFactory } from \"../../interfaces/iVaultFactory.sol\";\nimport { IFluidLiquidity } from \"../../../../liquidity/interfaces/iLiquidity.sol\";\nimport { StorageRead } from \"../../../../libraries/storageRead.sol\";\n\nimport { Structs } from \"./structs.sol\";\n\ninterface TokenInterface {\n function decimals() external view returns (uint8);\n}\n\ncontract ConstantVariables is StorageRead, Structs {\n /***********************************|\n | Constant Variables |\n |__________________________________*/\n\n address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n /// @dev collateral token address\n address internal immutable SUPPLY_TOKEN;\n /// @dev borrow token address\n address internal immutable BORROW_TOKEN;\n\n /// @dev Token decimals. For example wETH is 18 decimals\n uint8 internal immutable SUPPLY_DECIMALS;\n /// @dev Token decimals. For example USDC is 6 decimals\n uint8 internal immutable BORROW_DECIMALS;\n\n /// @dev VaultT1 AdminModule implemenation address\n address internal immutable ADMIN_IMPLEMENTATION;\n\n /// @dev VaultT1 Secondary implemenation (main2.sol) address\n address internal immutable SECONDARY_IMPLEMENTATION;\n\n /// @dev liquidity proxy contract address\n IFluidLiquidity public immutable LIQUIDITY;\n\n /// @dev vault factory contract address\n IFluidVaultFactory public immutable VAULT_FACTORY;\n\n uint public immutable VAULT_ID;\n\n uint internal constant X8 = 0xff;\n uint internal constant X10 = 0x3ff;\n uint internal constant X16 = 0xffff;\n uint internal constant X19 = 0x7ffff;\n uint internal constant X20 = 0xfffff;\n uint internal constant X24 = 0xffffff;\n uint internal constant X25 = 0x1ffffff;\n uint internal constant X30 = 0x3fffffff;\n uint internal constant X35 = 0x7ffffffff;\n uint internal constant X50 = 0x3ffffffffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n uint internal constant X96 = 0xffffffffffffffffffffffff;\n uint internal constant X128 = 0xffffffffffffffffffffffffffffffff;\n\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev slot ids in Liquidity contract. Helps in low gas fetch from liquidity contract by skipping delegate call\n bytes32 internal immutable LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT;\n bytes32 internal immutable LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT;\n bytes32 internal immutable LIQUIDITY_USER_SUPPLY_SLOT;\n bytes32 internal immutable LIQUIDITY_USER_BORROW_SLOT;\n\n /// @notice returns all Vault constants\n function constantsView() external view returns (ConstantViews memory constantsView_) {\n constantsView_.liquidity = address(LIQUIDITY);\n constantsView_.factory = address(VAULT_FACTORY);\n constantsView_.adminImplementation = ADMIN_IMPLEMENTATION;\n constantsView_.secondaryImplementation = SECONDARY_IMPLEMENTATION;\n constantsView_.supplyToken = SUPPLY_TOKEN;\n constantsView_.borrowToken = BORROW_TOKEN;\n constantsView_.supplyDecimals = SUPPLY_DECIMALS;\n constantsView_.borrowDecimals = BORROW_DECIMALS;\n constantsView_.vaultId = VAULT_ID;\n constantsView_.liquiditySupplyExchangePriceSlot = LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT;\n constantsView_.liquidityBorrowExchangePriceSlot = LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT;\n constantsView_.liquidityUserSupplySlot = LIQUIDITY_USER_SUPPLY_SLOT;\n constantsView_.liquidityUserBorrowSlot = LIQUIDITY_USER_BORROW_SLOT;\n }\n\n constructor(ConstantViews memory constants_) {\n LIQUIDITY = IFluidLiquidity(constants_.liquidity);\n VAULT_FACTORY = IFluidVaultFactory(constants_.factory);\n VAULT_ID = constants_.vaultId;\n\n SUPPLY_TOKEN = constants_.supplyToken;\n BORROW_TOKEN = constants_.borrowToken;\n SUPPLY_DECIMALS = constants_.supplyDecimals;\n BORROW_DECIMALS = constants_.borrowDecimals;\n\n // @dev those slots are calculated in the deploymentLogics / VaultFactory\n LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT = constants_.liquiditySupplyExchangePriceSlot;\n LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT = constants_.liquidityBorrowExchangePriceSlot;\n LIQUIDITY_USER_SUPPLY_SLOT = constants_.liquidityUserSupplySlot;\n LIQUIDITY_USER_BORROW_SLOT = constants_.liquidityUserBorrowSlot;\n\n ADMIN_IMPLEMENTATION = constants_.adminImplementation;\n SECONDARY_IMPLEMENTATION = constants_.secondaryImplementation;\n }\n}\n" }, "contracts/protocols/vault/vaultT1/coreModule/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Events {\n /// @notice emitted when an operate() method is executed that changes collateral (`colAmt_`) / debt (debtAmt_`)\n /// amount for a `user_` position with `nftId_`. Receiver of any funds is the address `to_`.\n event LogOperate(address user_, uint256 nftId_, int256 colAmt_, int256 debtAmt_, address to_);\n\n /// @notice emitted when the exchange prices are updated in storage.\n event LogUpdateExchangePrice(uint256 supplyExPrice_, uint256 borrowExPrice_);\n\n /// @notice emitted when a liquidation has been executed.\n event LogLiquidate(address liquidator_, uint256 colAmt_, uint256 debtAmt_, address to_);\n\n /// @notice emitted when `absorb()` was executed to absorb bad debt.\n event LogAbsorb(uint colAbsorbedRaw_, uint debtAbsorbedRaw_);\n\n /// @notice emitted when a `rebalance()` has been executed, balancing out total supply / borrow between Vault\n /// and Fluid Liquidity pools.\n /// if `colAmt_` is positive then loss, meaning transfer from rebalancer address to vault and deposit.\n /// if `colAmt_` is negative then profit, meaning withdrawn from vault and sent to rebalancer address.\n /// if `debtAmt_` is positive then profit, meaning borrow from vault and sent to rebalancer address.\n /// if `debtAmt_` is negative then loss, meaning transfer from rebalancer address to vault and payback.\n event LogRebalance(int colAmt_, int debtAmt_);\n}\n" }, "contracts/protocols/vault/vaultT1/coreModule/helpers.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"../common/variables.sol\";\nimport { ConstantVariables } from \"./constantVariables.sol\";\nimport { Events } from \"./events.sol\";\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { BigMathVault } from \"../../../../libraries/bigMathVault.sol\";\nimport { LiquidityCalcs } from \"../../../../libraries/liquidityCalcs.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { Error } from \"../../error.sol\";\n\n/// @dev Fluid vault protocol helper methods. Mostly used for `operate()` and `liquidate()` methods of CoreModule.\nabstract contract Helpers is Variables, ConstantVariables, Events, Error {\n using BigMathMinified for uint256;\n using BigMathVault for uint256;\n\n /// @notice Calculates new vault exchange prices. Does not update values in storage.\n /// @param vaultVariables2_ exactly same as vaultVariables2 from storage\n /// @return liqSupplyExPrice_ latest liquidity's supply token supply exchange price\n /// @return liqBorrowExPrice_ latest liquidity's borrow token borrow exchange price\n /// @return vaultSupplyExPrice_ latest vault's supply token exchange price\n /// @return vaultBorrowExPrice_ latest vault's borrow token exchange price\n function updateExchangePrices(\n uint256 vaultVariables2_\n )\n public\n view\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n )\n {\n // Fetching last stored rates\n uint rates_ = rates;\n\n (liqSupplyExPrice_, ) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT)\n );\n (, liqBorrowExPrice_) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT)\n );\n\n uint256 oldLiqSupplyExPrice_ = (rates_ & X64);\n uint256 oldLiqBorrowExPrice_ = ((rates_ >> 64) & X64);\n if (liqSupplyExPrice_ < oldLiqSupplyExPrice_ || liqBorrowExPrice_ < oldLiqBorrowExPrice_) {\n // new liquidity exchange price is < than the old one. liquidity exchange price should only ever increase.\n // If not, something went wrong and avoid proceeding with unknown outcome.\n revert FluidVaultError(ErrorTypes.VaultT1__LiquidityExchangePriceUnexpected);\n }\n\n // liquidity Exchange Prices always increases in next block. Hence substraction with old will never be negative\n // uint64 * 1e18 is the max the number that could be\n unchecked {\n // Calculating increase in supply exchange price w.r.t last stored liquidity's exchange price\n // vaultSupplyExPrice_ => supplyIncreaseInPercent_\n vaultSupplyExPrice_ = ((((liqSupplyExPrice_ * 1e18) / oldLiqSupplyExPrice_) - 1e18) *\n (vaultVariables2_ & X16)) / 10000; // supply rate magnifier\n\n // Calculating increase in borrow exchange price w.r.t last stored liquidity's exchange price\n // vaultBorrowExPrice_ => borrowIncreaseInPercent_\n vaultBorrowExPrice_ = ((((liqBorrowExPrice_ * 1e18) / oldLiqBorrowExPrice_) - 1e18) *\n ((vaultVariables2_ >> 16) & X16)) / 10000; // borrow rate magnifier\n\n // It's extremely hard the exchange prices to overflow even in 100 years but if it does it's not an\n // issue here as we are not updating on storage\n // (rates_ >> 128) & X64) -> last stored vault's supply token exchange price\n vaultSupplyExPrice_ = (((rates_ >> 128) & X64) * (1e18 + vaultSupplyExPrice_)) / 1e18;\n // (rates_ >> 192) -> last stored vault's borrow token exchange price (no need to mask with & X64 as it is anyway max 64 bits)\n vaultBorrowExPrice_ = ((rates_ >> 192) * (1e18 + vaultBorrowExPrice_)) / 1e18;\n }\n }\n\n /// note admin module is also calling this function self call\n /// @dev updating exchange price on storage. Only need to update on storage when changing supply or borrow magnifier\n function updateExchangePricesOnStorage()\n public\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n )\n {\n (liqSupplyExPrice_, liqBorrowExPrice_, vaultSupplyExPrice_, vaultBorrowExPrice_) = updateExchangePrices(\n vaultVariables2\n );\n\n if (\n liqSupplyExPrice_ > X64 || liqBorrowExPrice_ > X64 || vaultSupplyExPrice_ > X64 || vaultBorrowExPrice_ > X64\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExchangePriceOverFlow);\n }\n\n // Updating in storage\n rates =\n liqSupplyExPrice_ |\n (liqBorrowExPrice_ << 64) |\n (vaultSupplyExPrice_ << 128) |\n (vaultBorrowExPrice_ << 192);\n\n emit LogUpdateExchangePrice(vaultSupplyExPrice_, vaultBorrowExPrice_);\n }\n\n /// @dev fetches new user's position after liquidation. The new liquidated position's debt is decreased by 0.01%\n /// to make sure that branch's liquidity never becomes 0 as if it would have gotten 0 then there will be multiple cases that we would need to tackle.\n /// @param positionTick_ position's tick when it was last updated through operate\n /// @param positionTickId_ position's tick Id. This stores the debt factor and branch to make the first connection\n /// @param positionRawDebt_ position's raw debt when it was last updated through operate\n /// @param tickData_ position's tick's tickData just for minor comparison to know if data is moved to tick Id or is still in tick data\n /// @return final tick position after all the liquidation\n /// @return final debt of position after all the liquidation\n /// @return positionRawCol_ final collateral of position after all the liquidation\n /// @return branchId_ final branch's ID where the position is at currently\n /// @return branchData_ final branch's data where the position is at currently\n function fetchLatestPosition(\n int256 positionTick_,\n uint256 positionTickId_,\n uint256 positionRawDebt_,\n uint256 tickData_\n )\n public\n view\n returns (\n int256, // positionTick_\n uint256, // positionRawDebt_\n uint256 positionRawCol_,\n uint256 branchId_,\n uint256 branchData_\n )\n {\n uint256 initialPositionRawDebt_ = positionRawDebt_;\n uint256 connectionFactor_;\n bool isFullyLiquidated_;\n\n // Checking if tick's total ID = user's tick ID\n if (((tickData_ >> 1) & X24) == positionTickId_) {\n // fetching from tick data itself\n isFullyLiquidated_ = ((tickData_ >> 25) & 1) == 1;\n branchId_ = (tickData_ >> 26) & X30;\n connectionFactor_ = (tickData_ >> 56) & X50;\n } else {\n {\n uint256 tickLiquidationData_;\n unchecked {\n // Fetching tick's liquidation data. One variable contains data of 3 IDs. Tick Id mapping is starting from 1.\n tickLiquidationData_ =\n tickId[positionTick_][(positionTickId_ + 2) / 3] >>\n (((positionTickId_ + 2) % 3) * 85);\n }\n\n isFullyLiquidated_ = (tickLiquidationData_ & 1) == 1;\n branchId_ = (tickLiquidationData_ >> 1) & X30;\n connectionFactor_ = (tickLiquidationData_ >> 31) & X50;\n }\n }\n\n // data of branch\n branchData_ = branchData[branchId_];\n\n if (isFullyLiquidated_) {\n positionTick_ = type(int).min;\n positionRawDebt_ = 0;\n } else {\n // Below information about connection debt factor\n // If branch is merged, Connection debt factor is used to multiply in order to get perfect liquidation of user\n // For example: Considering user was at the top.\n // In first branch, the user liquidated to debt factor 0.5 and then branch got merged (branching starting from 1)\n // In second branch, it got liquidated to 0.4 but when the above branch merged the debt factor on this branch was 0.6\n // Meaning on 1st branch, user got liquidated by 50% & on 2nd by 33.33%. So a total of 66.6%.\n // What we will set a connection factor will be 0.6/0.5 = 1.2\n // So now to get user's position, this is what we'll do:\n // finalDebt = (0.4 / (1 * 1.2)) * debtBeforeLiquidation\n // 0.4 is current active branch's minima debt factor\n // 1 is debt factor from where user started\n // 1.2 is connection factor which we found out through 0.6 / 0.5\n while ((branchData_ & 3) == 2) {\n // If true then the branch is merged\n\n // userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor aka adjustmentDebtFactor\n connectionFactor_ = connectionFactor_.mulBigNumber(((branchData_ >> 116) & X50));\n if (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR) break; // user ~100% liquidated\n // Note we don't need updated branch data in case of 100% liquidated so saving gas for fetching it\n\n // Fetching new branch data\n branchId_ = (branchData_ >> 166) & X30; // Link to base branch of current branch\n branchData_ = branchData[branchId_];\n }\n // When the while loop breaks meaning the branch now has minima Debt Factor or is a closed branch;\n\n if (((branchData_ & 3) == 3) || (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR)) {\n // Branch got closed (or user liquidated ~100%). Hence make the user's position 0\n // Rare cases to get into this situation\n // Branch can get close often but once closed it's tricky that some user might come iterating through there\n // If a user comes then that user will be very mini user like some cents probably\n positionTick_ = type(int).min;\n positionRawDebt_ = 0;\n } else {\n // If branch is not merged, the main branch it's connected to then it'll have minima debt factor\n\n // position debt = debt * base branch minimaDebtFactor / connectionFactor\n positionRawDebt_ = positionRawDebt_.mulDivNormal(\n (branchData_ >> 116) & X50, // minimaDebtFactor\n connectionFactor_\n );\n\n unchecked {\n // Reducing user's liquidity by 0.01% if user got liquidated.\n // As this will make sure that the branch always have some debt even if all liquidated user left\n // This saves a lot more logics & consideration on Operate function\n // if we don't do this then we have to add logics related to closing the branch and factor connections accordingly.\n if (positionRawDebt_ > (initialPositionRawDebt_ / 100)) {\n positionRawDebt_ = (positionRawDebt_ * 9999) / 10000;\n } else {\n // if user debt reduced by more than 99% in liquidation then making user as fully liquidated\n positionRawDebt_ = 0;\n }\n }\n\n {\n if (positionRawDebt_ > 0) {\n // positionTick_ -> read minima tick of branch\n unchecked {\n positionTick_ = branchData_ & 4 == 4\n ? int((branchData_ >> 3) & X19)\n : -int((branchData_ >> 3) & X19);\n }\n // Calculating user's collateral\n uint256 ratioAtTick_ = TickMath.getRatioAtTick(int24(positionTick_));\n uint256 ratioOneLess_;\n unchecked {\n ratioOneLess_ = (ratioAtTick_ * 10000) / 10015;\n }\n // formula below for better readability:\n // length = ratioAtTick_ - ratioOneLess_\n // ratio = ratioOneLess_ + (length * positionPartials_) / X30\n // positionRawCol_ = (positionRawDebt_ * (1 << 96)) / ratio_\n positionRawCol_ =\n (positionRawDebt_ * TickMath.ZERO_TICK_SCALED_RATIO) /\n (ratioOneLess_ + ((ratioAtTick_ - ratioOneLess_) * ((branchData_ >> 22) & X30)) / X30);\n } else {\n positionTick_ = type(int).min;\n }\n }\n }\n }\n return (positionTick_, positionRawDebt_, positionRawCol_, branchId_, branchData_);\n }\n\n /// @dev sets `tick_` as having debt or no debt in storage `tickHasDebt` depending on `addOrRemove_`\n /// @param tick_ tick to add or remove from tickHasDebt\n /// @param addOrRemove_ if true then add else remove\n function _updateTickHasDebt(int tick_, bool addOrRemove_) internal {\n // Positive mapID_ starts from 0 & above and negative starts below 0.\n // tick 0 to 255 will have mapId_ as 0 while tick -256 to -1 will have mapId_ as -1.\n unchecked {\n int mapId_ = tick_ < 0 ? ((tick_ + 1) / 256) - 1 : tick_ / 256;\n\n // in case of removing:\n // (tick == 255) tickHasDebt[mapId_] - 1 << 255\n // (tick == 0) tickHasDebt[mapId_] - 1 << 0\n // (tick == -1) tickHasDebt[mapId_] - 1 << 255\n // (tick == -256) tickHasDebt[mapId_] - 1 << 0\n // in case of adding:\n // (tick == 255) tickHasDebt[mapId_] - 1 << 255\n // (tick == 0) tickHasDebt[mapId_] - 1 << 0\n // (tick == -1) tickHasDebt[mapId_] - 1 << 255\n // (tick == -256) tickHasDebt[mapId_] - 1 << 0\n uint position_ = uint(tick_ - (mapId_ * 256));\n\n tickHasDebt[mapId_] = addOrRemove_\n ? tickHasDebt[mapId_] | (1 << position_)\n : tickHasDebt[mapId_] & ~(1 << position_);\n }\n }\n\n /// @dev gets next perfect top tick (tick which is not liquidated)\n /// @param topTick_ current top tick which will no longer be top tick\n /// @return nextTick_ next top tick which will become the new top tick\n function _fetchNextTopTick(int topTick_) internal view returns (int nextTick_) {\n int mapId_;\n uint tickHasDebt_;\n\n unchecked {\n mapId_ = topTick_ < 0 ? ((topTick_ + 1) / 256) - 1 : topTick_ / 256;\n uint bitsToRemove_ = uint(-topTick_ + (mapId_ * 256 + 256));\n // Removing current top tick from tickHasDebt\n tickHasDebt_ = (tickHasDebt[mapId_] << bitsToRemove_) >> bitsToRemove_;\n\n // For last user remaining in vault there could be a lot of iterations in the while loop.\n // Chances of this to happen is extremely low (like ~0%)\n while (true) {\n if (tickHasDebt_ > 0) {\n nextTick_ = mapId_ * 256 + int(tickHasDebt_.mostSignificantBit()) - 1;\n break;\n }\n\n // Reducing mapId_ by 1 in every loop; if it reaches to -129 then no filled tick exist, meaning it's the last tick\n if (--mapId_ == -129) {\n nextTick_ = type(int).min;\n break;\n }\n\n tickHasDebt_ = tickHasDebt[mapId_];\n }\n }\n }\n\n /// @dev adding debt to a particular tick\n /// @param totalColRaw_ total raw collateral of position\n /// @param netDebtRaw_ net raw debt (total debt - dust debt)\n /// @return tick_ tick where the debt is being added\n /// @return tickId_ tick current id\n /// @return userRawDebt_ user's total raw debt\n /// @return rawDust_ dust debt used for adjustment\n function _addDebtToTickWrite(\n uint256 totalColRaw_,\n uint256 netDebtRaw_ // debtRaw - dust\n ) internal returns (int256 tick_, uint256 tickId_, uint256 userRawDebt_, uint256 rawDust_) {\n if (netDebtRaw_ < 10000) {\n // thrown if user's debt is too low\n revert FluidVaultError(ErrorTypes.VaultT1__UserDebtTooLow);\n }\n // tick_ & ratio_ returned from library is round down. Hence increasing it by 1 and increasing ratio by 1 tick.\n uint ratio_ = (netDebtRaw_ * TickMath.ZERO_TICK_SCALED_RATIO) / totalColRaw_;\n (tick_, ratio_) = TickMath.getTickAtRatio(ratio_);\n unchecked {\n ++tick_;\n ratio_ = (ratio_ * 10015) / 10000;\n }\n userRawDebt_ = (ratio_ * totalColRaw_) >> 96;\n rawDust_ = userRawDebt_ - netDebtRaw_;\n\n // Current state of tick\n uint256 tickData_ = tickData[tick_];\n tickId_ = (tickData_ >> 1) & X24;\n\n uint tickNewDebt_;\n if (tickId_ > 0 && tickData_ & 1 == 0) {\n // Current debt in the tick\n uint256 tickExistingRawDebt_ = (tickData_ >> 25) & X64;\n tickExistingRawDebt_ = (tickExistingRawDebt_ >> 8) << (tickExistingRawDebt_ & X8);\n\n // Tick's already initialized and not liquidated. Hence simply add the debt\n tickNewDebt_ = tickExistingRawDebt_ + userRawDebt_;\n if (tickExistingRawDebt_ == 0) {\n // Adding tick into tickHasDebt\n _updateTickHasDebt(tick_, true);\n }\n } else {\n // Liquidation happened or tick getting initialized for the very first time.\n if (tickId_ > 0) {\n // Meaning a liquidation happened. Hence move the data to tickID\n unchecked {\n uint tickMap_ = (tickId_ + 2) / 3;\n // Adding 2 in ID so we can get right mapping ID. For example for ID 1, 2 & 3 mapping should be 1 and so on..\n // For example shift for id 1 should be 0, for id 2 should be 85, for id 3 it should be 170 and so on..\n tickId[tick_][tickMap_] =\n tickId[tick_][tickMap_] |\n ((tickData_ >> 25) << (((tickId_ + 2) % 3) * 85));\n }\n }\n // Increasing total ID by one\n unchecked {\n ++tickId_;\n }\n tickNewDebt_ = userRawDebt_;\n\n // Adding tick into tickHasDebt\n _updateTickHasDebt(tick_, true);\n }\n if (tickNewDebt_ < 10000) {\n // thrown if tick's debt/liquidity is too low\n revert FluidVaultError(ErrorTypes.VaultT1__TickDebtTooLow);\n }\n tickData[tick_] = (tickId_ << 1) | (tickNewDebt_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 25);\n }\n\n /// @dev sets new top tick. If it comes to this function then that means current top tick is perfect tick.\n /// if next top tick is liquidated then unitializes the current non liquidated branch and make the liquidated branch as current branch\n /// @param topTick_ current top tick\n /// @param vaultVariables_ vaultVariables of storage but with newer updates\n /// @return newVaultVariables_ newVaultVariables_ updated vault variable internally to this function\n /// @return newTopTick_ new top tick\n function _setNewTopTick(\n int topTick_,\n uint vaultVariables_\n ) internal returns (uint newVaultVariables_, int newTopTick_) {\n // This function considers that the current top tick was not liquidated\n // Overall flow of function:\n // if new top tick liquidated (aka base branch's minima tick) -> Close the current branch and make base branch as current branch\n // if new top tick not liquidated -> update things in current branch.\n // if new top tick is not liquidated and same tick exist in base branch then tick is considered as not liquidated.\n\n uint branchId_ = (vaultVariables_ >> 22) & X30; // branch id of current branch\n\n uint256 branchData_ = branchData[branchId_];\n int256 baseBranchMinimaTick_;\n if ((branchData_ >> 196) & 1 == 1) {\n baseBranchMinimaTick_ = int((branchData_ >> 197) & X19);\n } else {\n unchecked {\n baseBranchMinimaTick_ = -int((branchData_ >> 197) & X19);\n }\n if (baseBranchMinimaTick_ == 0) {\n // meaning the current branch is the master branch\n baseBranchMinimaTick_ = type(int).min;\n }\n }\n\n // Returns type(int).min if no top tick exist\n int nextTopTickNotLiquidated_ = _fetchNextTopTick(topTick_);\n\n newTopTick_ = baseBranchMinimaTick_ > nextTopTickNotLiquidated_\n ? baseBranchMinimaTick_\n : nextTopTickNotLiquidated_;\n\n if (newTopTick_ == type(int).min) {\n // if this happens that means this was the last user of the vault :(\n vaultVariables_ = vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001;\n } else if (newTopTick_ == nextTopTickNotLiquidated_) {\n // New top tick exist in current non liquidated branch\n if (newTopTick_ < 0) {\n unchecked {\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001) |\n (uint(-newTopTick_) << 3);\n }\n } else {\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001) |\n 4 | // setting top tick as positive\n (uint(newTopTick_) << 3);\n }\n } else {\n // if this happens that means base branch exists & is the next top tick\n // Remove current non liquidated branch as active.\n // Not deleting here as it's going to get initialize again whenever a new top tick comes\n branchData[branchId_] = 0;\n // Inserting liquidated branch's minima tick\n unchecked {\n vaultVariables_ =\n (vaultVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00000000000000000001) |\n 2 | // Setting top tick as liquidated\n (((branchData_ >> 196) & X20) << 2) | // new current top tick = base branch minima tick\n (((branchData_ >> 166) & X30) << 22) | // new current branch id = base branch id\n ((branchId_ - 1) << 52); // reduce total branch id by 1\n }\n }\n\n newVaultVariables_ = vaultVariables_;\n }\n\n constructor(ConstantViews memory constants_) ConstantVariables(constants_) {}\n}\n" }, "contracts/protocols/vault/vaultT1/coreModule/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n\nimport { IFluidOracle } from \"../../../../oracle/fluidOracle.sol\";\n\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { BigMathVault } from \"../../../../libraries/bigMathVault.sol\";\nimport { LiquidityCalcs } from \"../../../../libraries/liquidityCalcs.sol\";\nimport { SafeTransfer } from \"../../../../libraries/safeTransfer.sol\";\n\nimport { Helpers } from \"./helpers.sol\";\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\n\n/// @notice Fluid \"VaultT1\" (Vault Type 1). Fluid vault protocol main contract.\n/// Fluid Vault protocol is a borrow / lending protocol, allowing users to create collateral / borrow positions.\n/// All funds are deposited into / borrowed from Fluid Liquidity layer.\n/// Positions are represented through NFTs minted by the VaultFactory.\n/// Deployed by \"VaultFactory\" and linked together with VaultT1 AdminModule `ADMIN_IMPLEMENTATION` and\n/// FluidVaultT1Secondary (main2.sol) `SECONDARY_IMPLEMENTATION`.\n/// AdminModule & FluidVaultT1Secondary methods are delegateCalled, if the msg.sender has the required authorization.\n/// This contract links to an Oracle, which is used to assess collateral / debt value. Oracles implement the\n/// \"FluidOracle\" base contract and return the price in 1e27 precision.\n/// @dev For view methods / accessing data, use the \"VaultResolver\" periphery contract.\n//\n// vaults can only be deployed for tokens that are listed at Liquidity (constructor reverts otherwise\n// if either the exchange price for the supply token or the borrow token is still not set at Liquidity).\ncontract FluidVaultT1 is Helpers {\n using BigMathMinified for uint256;\n using BigMathVault for uint256;\n\n /// @dev Single function which handles supply, withdraw, borrow & payback\n /// @param nftId_ NFT ID for interaction. If 0 then create new NFT/position.\n /// @param newCol_ new collateral. If positive then deposit, if negative then withdraw, if 0 then do nohing\n /// @param newDebt_ new debt. If positive then borrow, if negative then payback, if 0 then do nohing\n /// @param to_ address where withdraw or borrow should go. If address(0) then msg.sender\n /// @return nftId_ if 0 then this returns the newly created NFT Id else returns the same NFT ID\n /// @return newCol_ final supply amount. Mainly if max withdraw using type(int).min then this is useful to get perfect amount else remain same as newCol_\n /// @return newDebt_ final borrow amount. Mainly if max payback using type(int).min then this is useful to get perfect amount else remain same as newDebt_\n function operate(\n uint256 nftId_, // if 0 then new position\n int256 newCol_, // if negative then withdraw\n int256 newDebt_, // if negative then payback\n address to_ // address at which the borrow & withdraw amount should go to. If address(0) then it'll go to msg.sender\n )\n public\n payable\n returns (\n uint256, // nftId_\n int256, // final supply amount. if - then withdraw\n int256 // final borrow amount. if - then payback\n )\n {\n uint256 vaultVariables_ = vaultVariables;\n // re-entrancy check\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n if (\n (newCol_ == 0 && newDebt_ == 0) ||\n // withdrawal or deposit cannot be too small\n ((newCol_ != 0) && (newCol_ > -10000 && newCol_ < 10000)) ||\n // borrow or payback cannot be too small\n ((newDebt_ != 0) && (newDebt_ > -10000 && newDebt_ < 10000))\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOperateAmount);\n }\n\n // Check msg.value aligns with input amounts if supply or borrow token is native token.\n // Note that it's not possible for a vault to have both supply token and borrow token as native token.\n if (SUPPLY_TOKEN == NATIVE_TOKEN && newCol_ > 0) {\n if (uint(newCol_) != msg.value) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n } else if (msg.value > 0) {\n if (!(BORROW_TOKEN == NATIVE_TOKEN && newDebt_ < 0)) {\n // msg.value sent along for withdraw, borrow, or non-native token operations\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n }\n\n OperateMemoryVars memory o_;\n // Temporary variables used as helpers at many places\n uint256 temp_;\n uint256 temp2_;\n int256 temp3_;\n\n o_.vaultVariables2 = vaultVariables2;\n\n temp_ = (vaultVariables_ >> 2) & X20;\n unchecked {\n o_.topTick = (temp_ == 0) ? type(int).min : ((temp_ & 1) == 1)\n ? int((temp_ >> 1) & X19)\n : -int((temp_ >> 1) & X19);\n }\n\n {\n // Fetching user's position\n if (nftId_ == 0) {\n // creating new position.\n o_.tick = type(int).min;\n // minting new NFT vault for user.\n nftId_ = VAULT_FACTORY.mint(VAULT_ID, msg.sender);\n // Adding 1 in total positions. Total positions cannot exceed 32bits as NFT minting checks for that\n unchecked {\n vaultVariables_ = vaultVariables_ + (1 << 210);\n }\n } else {\n // Updating existing position\n\n // checking owner only in case of withdraw or borrow\n if ((newCol_ < 0 || newDebt_ > 0) && (VAULT_FACTORY.ownerOf(nftId_) != msg.sender)) {\n revert FluidVaultError(ErrorTypes.VaultT1__NotAnOwner);\n }\n\n // temp_ => user's position data\n temp_ = positionData[nftId_];\n\n if (temp_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__NftNotOfThisVault);\n }\n // temp2_ => user's supply amount\n temp2_ = (temp_ >> 45) & X64;\n // Converting big number into normal number\n o_.colRaw = (temp2_ >> 8) << (temp2_ & X8);\n // temp2_ => user's dust debt amount\n temp2_ = (temp_ >> 109) & X64;\n // Converting big number into normal number\n o_.dustDebtRaw = (temp2_ >> 8) << (temp2_ & X8);\n\n // 1 is supply & 0 is borrow\n if (temp_ & 1 == 1) {\n // only supply position (has no debt)\n o_.tick = type(int).min;\n } else {\n // borrow position (has collateral & debt)\n unchecked {\n o_.tick = temp_ & 2 == 2 ? int((temp_ >> 2) & X19) : -int((temp_ >> 2) & X19);\n }\n o_.tickId = (temp_ >> 21) & X24;\n }\n }\n }\n\n // Get latest updated Position's debt & supply (if position is with debt -> not new / supply position)\n if (o_.tick > type(int).min) {\n // if entering this if statement then temp_ here will always be user's position data\n // extracting collateral exponent\n temp_ = (temp_ >> 45) & X8;\n // if exponent is > 0 then rounding up the collateral just for calculating debt\n unchecked {\n temp_ = temp_ == 0 ? (o_.colRaw + 1) : o_.colRaw + (1 << temp_);\n }\n // fetch current debt\n o_.debtRaw = ((TickMath.getRatioAtTick(int24(o_.tick)) * temp_) >> 96) + 1;\n\n // Tick data from user's tick\n temp_ = tickData[o_.tick];\n\n // Checking if tick is liquidated (first bit 1) OR if the total IDs of tick is greater than user's tick ID\n if (((temp_ & 1) == 1) || (((temp_ >> 1) & X24) > o_.tickId)) {\n // User got liquidated\n (\n // returns the position of the user if the user got liquidated.\n o_.tick,\n o_.debtRaw,\n o_.colRaw,\n temp2_, // final branchId from liquidation where position exist right now\n o_.branchData\n ) = fetchLatestPosition(o_.tick, o_.tickId, o_.debtRaw, temp_);\n\n if (o_.debtRaw > o_.dustDebtRaw) {\n // temp_ => branch's Debt\n temp_ = (o_.branchData >> 52) & X64;\n temp_ = (temp_ >> 8) << (temp_ & X8);\n\n // o_.debtRaw should always be < branch's Debt (temp_).\n // Taking margin (0.01%) in fetchLatestPosition to make sure it's always less\n temp_ -= o_.debtRaw;\n if (temp_ < 100) {\n // explicitly making sure that branch debt/liquidity doesn't get super low.\n temp_ = 100;\n }\n // Inserting updated branch's debt\n branchData[temp2_] =\n (o_.branchData & 0xfffffffffffffffffffffffffffffffffff0000000000000000fffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52);\n\n unchecked {\n // Converted positionRawDebt_ in net position debt\n o_.debtRaw -= o_.dustDebtRaw;\n }\n } else {\n // Liquidated 100% or almost 100%\n // absorbing dust debt\n absorbedDustDebt = absorbedDustDebt + o_.dustDebtRaw - o_.debtRaw;\n o_.debtRaw = 0;\n o_.colRaw = 0;\n }\n } else {\n // User didn't got liquidated\n // Removing user's debt from tick data\n // temp2_ => debt in tick\n temp2_ = (temp_ >> 25) & X64;\n // below require can fail when a user liquidity is extremely low (talking about way less than even $1)\n // adding require meaning this vault user won't be able to interact unless someone makes the liquidity in tick as non 0.\n // reason of adding is the tick has already removed from everywhere. Can removing it again break something? Better to simply remove that case entirely\n if (temp2_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__TickIsEmpty);\n }\n // Converting big number into normal number\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n // debtInTick (temp2_) < debtToRemove (o_.debtRaw) that means minor precision error. Hence make the debtInTick as 0.\n // The precision error can be caused with Bigmath library limiting the precision to 2**56.\n unchecked {\n temp2_ = o_.debtRaw < temp2_ ? temp2_ - o_.debtRaw : 0;\n }\n\n if (temp2_ < 10000) {\n temp2_ = 0;\n // if debt becomes 0 then remove from tick has debt\n\n if (o_.tick == o_.topTick) {\n // if tick is top tick then current top tick is perfect tick -> fetch & set new top tick\n\n // Updating new top tick in vaultVariables_ and topTick_\n (vaultVariables_, o_.topTick) = _setNewTopTick(o_.topTick, vaultVariables_);\n }\n\n // Removing from tickHasDebt\n _updateTickHasDebt(o_.tick, false);\n }\n\n tickData[o_.tick] = (temp_ & X25) | (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 25);\n\n // Converted positionRawDebt_ in net position debt\n o_.debtRaw -= o_.dustDebtRaw;\n }\n o_.dustDebtRaw = 0;\n }\n\n // Setting the current tick into old tick as the position tick is going to change now.\n o_.oldTick = o_.tick;\n o_.oldColRaw = o_.colRaw;\n o_.oldNetDebtRaw = o_.debtRaw;\n\n {\n (o_.liquidityExPrice, , o_.supplyExPrice, o_.borrowExPrice) = updateExchangePrices(o_.vaultVariables2);\n\n {\n // supply or withdraw\n if (newCol_ > 0) {\n // supply new col, rounding down\n o_.colRaw += (uint256(newCol_) * EXCHANGE_PRICES_PRECISION) / o_.supplyExPrice;\n // final user's collateral should not be above 2**128 bits\n if (o_.colRaw > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n } else if (newCol_ < 0) {\n // if withdraw equals type(int).min then max withdraw\n if (newCol_ > type(int128).min) {\n // partial withdraw, rounding up removing extra wei from collateral\n temp3_ = ((newCol_ * int(EXCHANGE_PRICES_PRECISION)) / int256(o_.supplyExPrice)) - 1;\n unchecked {\n if (uint256(-temp3_) > o_.colRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessCollateralWithdrawal);\n }\n o_.colRaw -= uint256(-temp3_);\n }\n } else if (newCol_ == type(int).min) {\n // max withdraw, rounding up:\n // adding +1 to negative withdrawAmount newCol_ for safe rounding (reducing withdraw)\n newCol_ = -(int256((o_.colRaw * o_.supplyExPrice) / EXCHANGE_PRICES_PRECISION)) + 1;\n o_.colRaw = 0;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n }\n }\n {\n // borrow or payback\n if (newDebt_ > 0) {\n // borrow new debt, rounding up adding extra wei in debt\n temp_ = ((uint(newDebt_) * EXCHANGE_PRICES_PRECISION) / o_.borrowExPrice) + 1;\n // if borrow fee is 0 then it'll become temp_ + 0.\n // Only adding fee in o_.debtRaw and not in newDebt_ as newDebt_ is debt that needs to be borrowed from Liquidity\n // as we have added fee in debtRaw hence it will get added in user's position & vault's total borrow.\n // It can be collected with rebalance function.\n o_.debtRaw += temp_ + (temp_ * ((o_.vaultVariables2 >> 82) & X10)) / 10000;\n // final user's debt should not be above 2**128 bits\n if (o_.debtRaw > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n } else if (newDebt_ < 0) {\n // if payback equals type(int).min then max payback\n if (newDebt_ > type(int128).min) {\n // partial payback.\n // temp3_ => newDebt_ in raw terms, safe rounding up negative amount to rounding reduce payback\n temp3_ = (newDebt_ * int256(EXCHANGE_PRICES_PRECISION)) / int256(o_.borrowExPrice) + 1;\n unchecked {\n temp3_ = -temp3_;\n if (uint256(temp3_) > o_.debtRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessDebtPayback);\n }\n o_.debtRaw -= uint256(temp3_);\n }\n } else if (newDebt_ == type(int).min) {\n // max payback, rounding up amount that will be transferred in to pay back full debt:\n // subtracting -1 of negative debtAmount newDebt_ for safe rounding (increasing payback)\n newDebt_ = -(int256((o_.debtRaw * o_.borrowExPrice) / EXCHANGE_PRICES_PRECISION)) - 1;\n o_.debtRaw = 0;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n }\n }\n }\n\n // if position has no collateral or debt and user sends type(int).min for withdraw and payback then this results in 0\n // there's is no issue if it stays 0 but better to throw here to avoid checking for potential issues if there could be\n if (newCol_ == 0 && newDebt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOperateAmount);\n }\n\n // Assign new tick\n if (o_.debtRaw > 0) {\n // updating tickHasDebt in the below function if required\n // o_.debtRaw here is updated to new debt raw incl. dust debt (not net debt)\n unchecked {\n (o_.tick, o_.tickId, o_.debtRaw, o_.dustDebtRaw) = _addDebtToTickWrite(\n o_.colRaw,\n ((o_.debtRaw * 1000000001) / 1000000000) + 1\n );\n }\n\n if (newDebt_ < 0) {\n // anyone can payback debt of any position\n // hence, explicitly checking the debt should decrease\n if ((o_.debtRaw - o_.dustDebtRaw) > o_.oldNetDebtRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidPaybackOrDeposit);\n }\n }\n if ((newCol_ > 0) && (newDebt_ == 0)) {\n // anyone can deposit collateral in any position\n // Hence, explicitly checking that new ratio should be less than old ratio\n if (\n (((o_.debtRaw - o_.dustDebtRaw) * TickMath.ZERO_TICK_SCALED_RATIO) / o_.colRaw) >\n ((o_.oldNetDebtRaw * TickMath.ZERO_TICK_SCALED_RATIO) / o_.oldColRaw)\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidPaybackOrDeposit);\n }\n }\n\n if (o_.tick >= o_.topTick) {\n // Updating topTick in storage\n // temp_ => tick to insert in vault variables\n unchecked {\n temp_ = o_.tick < 0 ? uint(-o_.tick) << 1 : (uint(o_.tick) << 1) | 1;\n }\n if (vaultVariables_ & 2 == 0) {\n // Current branch not liquidated. Hence, just update top tick\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000) |\n (temp_ << 2);\n } else {\n // Current branch liquidated\n // Initialize a new branch\n // temp2_ => totalBranchId_\n unchecked {\n temp2_ = ((vaultVariables_ >> 52) & X30) + 1; // would take 34 years to overflow if a new branch is created every second\n }\n // Connecting new active branch with current active branch which is now base branch\n // Current top tick is now base branch's minima tick\n branchData[temp2_] =\n (((vaultVariables_ >> 22) & X30) << 166) | // current branch id set as base branch id\n (((vaultVariables_ >> 2) & X20) << 196); // current top tick set as base branch minima tick\n // Updating new vault variables in memory with new branch\n vaultVariables_ =\n (vaultVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00000000000000000000) |\n (temp_ << 2) | // new top tick\n (temp2_ << 22) | // new branch id\n (temp2_ << 52); // total branch ids\n }\n }\n } else {\n // debtRaw_ remains 0 in this situation\n // This kind of position will not have any tick. Meaning it'll be a supply position.\n o_.tick = type(int).min;\n }\n\n {\n if (newCol_ < 0 || newDebt_ > 0) {\n // withdraw or borrow\n if (to_ == address(0)) {\n to_ = msg.sender;\n }\n\n unchecked {\n // if debt is greater than 0 & transaction includes borrow or withdraw (incl. combinations such as deposit + borrow etc.)\n // -> check collateral factor\n // calc for net debt can be unchecked as o_.dustDebtRaw can not be > o_.debtRaw:\n // o_.dustDebtRaw is the result of o_.debtRaw - x where x > 0 see _addDebtToTickWrite()\n\n // Only fetch oracle if position is getting riskier or if borrowing is involved\n // if user is withdrawing and paying back in the same transaction such that the final ratio\n // is lower than initial then as well no need to check oracle aka user is doing payback & withdraw or deleverage\n if (o_.debtRaw > 0 && (\n o_.oldTick <= o_.tick ||\n (o_.debtRaw - o_.dustDebtRaw) > (((o_.oldNetDebtRaw * 1000000001) / 1000000000) + 1)\n )\n ) {\n // Oracle returns price at 100% ratio.\n // converting oracle 160 bits into oracle address\n // temp_ => debt price w.r.t to col in 1e27\n temp_ = IFluidOracle(address(uint160(o_.vaultVariables2 >> 96))).getExchangeRateOperate();\n // Note if price would come back as 0 `getTickAtRatio` will fail\n\n // reverting if oracle price is too high or lower than 1e9 to avoid precision issues\n if (temp_ > 1e54 || temp_ < 1e9) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOraclePrice);\n }\n\n // Converting price in terms of raw amounts\n temp_ = (temp_ * o_.supplyExPrice) / o_.borrowExPrice;\n\n // capping oracle pricing to 1e45 (#487RGF783GF: id reference for other similar cases in codebase)\n // This means we are restricting collateral price to never go above 1e45\n // Above 1e45 precisions gets too low for calculations\n // This can will never happen for all good token pairs (for example, WBTC/DAI pair when WBTC price is $1M, oracle price will come as 1e43)\n // Restricting oracle price doesn't pose any risk to protocol as we are capping collateral price, meaning if price is above 1e45\n // user is simply not able to borrow more\n if (temp_ > 1e45) {\n temp_ = 1e45;\n }\n\n // temp2_ => ratio at CF. CF is in 3 decimals. 900 = 90%\n temp2_ = ((temp_ * ((o_.vaultVariables2 >> 32) & X10)) / 1000);\n\n // Price from oracle is in 1e27 decimals. Converting it into (1 << 96) decimals\n temp2_ = ((temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27);\n\n // temp3_ => tickAtCF_\n (temp3_, ) = TickMath.getTickAtRatio(temp2_);\n if (o_.tick > temp3_) {\n // Above CF, user should only be allowed to reduce ratio either by paying debt or by depositing more collateral\n // Not comparing collateral as user can potentially use safe/deleverage to reduce tick & debt.\n // On use of safe/deleverage, collateral will decrease but debt will decrease as well making the overall position safer.\n revert FluidVaultError(ErrorTypes.VaultT1__PositionAboveCF);\n }\n }\n }\n\n }\n }\n\n {\n // Updating user's new position on storage\n // temp_ => tick to insert as user position tick\n if (o_.tick > type(int).min) {\n unchecked {\n temp_ = o_.tick < 0 ? (uint(-o_.tick) << 1) : ((uint(o_.tick) << 1) | 1);\n }\n } else {\n // if positionTick_ = type(int).min OR positionRawDebt_ == 0 then that means it's only supply position\n // (for case of positionRawDebt_ == 0, tick is set to type(int).min further up)\n temp_ = 0;\n }\n\n positionData[nftId_] =\n ((temp_ == 0) ? 1 : 0) | // setting if supply only position (1) or not (first bit)\n (temp_ << 1) |\n (o_.tickId << 21) |\n (o_.colRaw.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 45) |\n // dust debt is rounded down because user debt = debt - dustDebt. rounding up would mean we reduce user debt\n (o_.dustDebtRaw.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 109);\n }\n\n // Withdrawal gap to make sure there's always liquidity for liquidation\n // For example if withdrawal allowance is 15% on liquidity then we can limit operate's withdrawal allowance to 10%\n // this will allow liquidate function to get extra 5% buffer for potential liquidations.\n if (newCol_ < 0) {\n // extracting withdrawal gap which is in 0.1% precision.\n temp_ = (o_.vaultVariables2 >> 62) & X10;\n if (temp_ > 0) {\n // fetching user's supply slot data\n o_.userSupplyLiquidityData = LIQUIDITY.readFromStorage(LIQUIDITY_USER_SUPPLY_SLOT);\n\n // converting current user's supply from big number to normal\n temp2_ = (o_.userSupplyLiquidityData >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n\n // fetching liquidity's withdrawal limit\n temp3_ = int(LiquidityCalcs.calcWithdrawalLimitBeforeOperate(o_.userSupplyLiquidityData, temp2_));\n\n // max the number could go is vault's supply * 1000. Overflowing is almost impossible.\n unchecked {\n // (liquidityUserSupply - withdrawalGap - liquidityWithdrawaLimit) should be less than user's withdrawal\n if (\n (temp3_ > 0) &&\n (((int(temp2_ * (1000 - temp_)) / 1000)) - temp3_) <\n (((-newCol_) * int(EXCHANGE_PRICES_PRECISION)) / int(o_.liquidityExPrice))\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__WithdrawMoreThanOperateLimit);\n }\n }\n }\n }\n\n {\n // execute actions at Liquidity: deposit & payback is first and then withdraw & borrow\n if (newCol_ > 0) {\n // deposit\n LIQUIDITY.operate{ value: SUPPLY_TOKEN == NATIVE_TOKEN ? msg.value : 0 }(\n SUPPLY_TOKEN,\n newCol_,\n 0,\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n }\n if (newDebt_ < 0) {\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n unchecked {\n temp_ = uint(-newDebt_);\n if (msg.value > temp_) {\n SafeTransfer.safeTransferNative(msg.sender, msg.value - temp_);\n } else if (msg.value < temp_) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n }\n } else {\n temp_ = 0;\n }\n // payback\n LIQUIDITY.operate{ value: temp_ }(\n BORROW_TOKEN,\n 0,\n newDebt_,\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n }\n if (newCol_ < 0) {\n // withdraw\n LIQUIDITY.operate(SUPPLY_TOKEN, newCol_, 0, to_, address(0), new bytes(0));\n }\n if (newDebt_ > 0) {\n // borrow\n LIQUIDITY.operate(BORROW_TOKEN, 0, newDebt_, address(0), to_, new bytes(0));\n }\n }\n\n {\n // Updating vault variables on storage\n\n // Calculating new total collateral & total debt.\n temp_ = (vaultVariables_ >> 82) & X64;\n temp_ = ((temp_ >> 8) << (temp_ & X8)) + o_.colRaw - o_.oldColRaw;\n temp2_ = (vaultVariables_ >> 146) & X64;\n temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) + (o_.debtRaw - o_.dustDebtRaw) - o_.oldNetDebtRaw;\n // Updating vault variables on storage. This will also reentrancy 0 back again\n // Converting total supply & total borrow in 64 bits (56 | 8) bignumber\n vaultVariables =\n (vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply\n (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow\n }\n\n emit LogOperate(msg.sender, nftId_, newCol_, newDebt_, to_);\n\n return (nftId_, newCol_, newDebt_);\n }\n\n /// @dev allows to liquidate all bad debt of all users at once. Liquidator can also liquidate partially any amount they want.\n /// @param debtAmt_ total debt to liquidate (aka debt token to swap into collateral token)\n /// @param colPerUnitDebt_ minimum collateral token per unit of debt in 1e18 decimals\n /// @param to_ address at which collateral token should go to.\n /// If dead address (0x000000000000000000000000000000000000dEaD) then reverts with custom error \"FluidLiquidateResult\"\n /// returning the actual collateral and actual debt liquidated. Useful to find max liquidatable amounts via try / catch.\n /// @param absorb_ if true then liquidate from absorbed first\n /// @return actualDebtAmt_ if liquidator sends debtAmt_ more than debt remaining to liquidate then actualDebtAmt_ changes from debtAmt_ else remains same\n /// @return actualColAmt_ total liquidated collateral which liquidator will get\n function liquidate(\n uint256 debtAmt_,\n uint256 colPerUnitDebt_, // min collateral needed per unit of debt in 1e18\n address to_,\n bool absorb_\n ) public payable returns (uint actualDebtAmt_, uint actualColAmt_) {\n LiquidateMemoryVars memory memoryVars_;\n\n uint vaultVariables_ = vaultVariables;\n\n // ############# turning re-entrancy bit on #############\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n if ((msg.value != debtAmt_) && (to_ != 0x000000000000000000000000000000000000dEaD)) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueLiquidate);\n }\n } else if (msg.value > 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueLiquidate);\n }\n\n memoryVars_.vaultVariables2 = vaultVariables2;\n\n if (((vaultVariables_ >> 2) & X20) == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__TopTickDoesNotExist);\n }\n\n // Below are exchange prices of vaults\n (, , memoryVars_.supplyExPrice, memoryVars_.borrowExPrice) = updateExchangePrices(memoryVars_.vaultVariables2);\n\n CurrentLiquidity memory currentData_;\n BranchData memory branch_;\n // Temporary holder variables, used many times for different small things\n uint temp_;\n uint temp2_;\n\n {\n // ############# Oracle related stuff #############\n // Col price w.r.t debt. For example: 1 ETH = 1000 DAI\n // temp_ -> debtPerCol\n temp_ = IFluidOracle(address(uint160(memoryVars_.vaultVariables2 >> 96))).getExchangeRateLiquidate(); // Price in 27 decimals\n\n // not reverting if oracle price is lower than 1e9 as it can pause potential liquidation in this edge case situations\n if (temp_ > 1e54 || temp_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOraclePrice);\n }\n\n unchecked {\n // temp_ -> debtPerCol Converting in terms of raw amount\n temp_ = (temp_ * memoryVars_.supplyExPrice) / memoryVars_.borrowExPrice;\n\n // capping oracle pricing to 1e45\n // Reason mentioned at (search: #487RGF783GF)\n if (temp_ > 1e45) {\n temp_ = 1e45;\n }\n // temp2_ -> Raw colPerDebt_ in 27 decimals\n temp2_ = 1e54 / temp_;\n\n // temp2_ can never be > 1e54\n // Oracle price should never be > 1e54\n // Liquidation penalty in 4 decimals (1e2 = 1%) (max: 10.23%) -> (vaultVariables2_ >> 72) & X10\n currentData_.colPerDebt = (temp2_ * (10000 + ((memoryVars_.vaultVariables2 >> 72) & X10))) / 10000;\n\n // get liquidiation tick (tick at liquidation threshold ratio)\n // Liquidation threshold in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 42) & X10\n // Dividing by 1e27 to convert temp_ into normal number\n temp_ = ((temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27);\n // temp2_ -> liquidationRatio_\n temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 42) & X10)) / 1000;\n }\n (memoryVars_.liquidationTick, ) = TickMath.getTickAtRatio(temp2_);\n\n // get liquidiation max limit tick (tick at liquidation max limit ratio)\n // Max limit in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 52) & X10\n // temp2_ -> maxRatio_\n unchecked {\n temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 52) & X10)) / 1000;\n }\n (memoryVars_.maxTick, ) = TickMath.getTickAtRatio(temp2_);\n }\n\n // extracting top tick as top tick will be the current tick\n unchecked {\n currentData_.tick = (vaultVariables_ & 4) == 4\n ? int256((vaultVariables_ >> 3) & X19)\n : -int256((vaultVariables_ >> 3) & X19);\n }\n\n if (currentData_.tick > memoryVars_.maxTick) {\n // absorbing all the debt above maxTick if available\n vaultVariables_ = (abi.decode(_spell(SECONDARY_IMPLEMENTATION, abi.encodeWithSignature(\"absorb(uint256,int256)\", vaultVariables_, memoryVars_.maxTick)), (uint256)));\n\n // updating current tick to new topTick after absorb\n unchecked {\n currentData_.tick = (vaultVariables_ & 4) == 4\n ? int256((vaultVariables_ >> 3) & X19)\n : -int256((vaultVariables_ >> 3) & X19);\n }\n if (debtAmt_ == 0) {\n // updating vault variables on storage as the transaction was for only absorb\n vaultVariables = vaultVariables_;\n return (0, 0);\n }\n }\n\n if (debtAmt_ < 10000 || debtAmt_ > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidationAmt);\n }\n\n // setting up status if top tick is liquidated or not\n currentData_.tickStatus = vaultVariables_ & 2 == 0 ? 1 : 2;\n // Tick info is mainly used as a place holder to store temporary tick related data\n // (it can be current or ref using same memory variable)\n TickData memory tickInfo_;\n tickInfo_.tick = currentData_.tick;\n\n {\n // ############# Setting current branch in memory #############\n\n // Updating branch related data\n branch_.id = (vaultVariables_ >> 22) & X30;\n branch_.data = branchData[branch_.id];\n branch_.debtFactor = (branch_.data >> 116) & X50;\n if (branch_.debtFactor == 0) {\n // Initializing branch debt factor. 35 | 15 bit number. Where full 35 bits and 15th bit is occupied.\n // Making the total number as (2**35 - 1) << 2**14.\n // note: initial debt factor can be any number.\n branch_.debtFactor = ((X35 << 15) | (1 << 14));\n }\n // fetching base branch's minima tick. if 0 that means it's a master branch\n temp_ = (branch_.data >> 196) & X20;\n if (temp_ > 0) {\n unchecked {\n branch_.minimaTick = (temp_ & 1) == 1 ? int256((temp_ >> 1) & X19) : -int256((temp_ >> 1) & X19);\n }\n } else {\n branch_.minimaTick = type(int).min;\n }\n }\n\n // debtAmt_ should be less than 2**128 & EXCHANGE_PRICES_PRECISION is 1e12\n unchecked {\n currentData_.debtRemaining = (debtAmt_ * EXCHANGE_PRICES_PRECISION) / memoryVars_.borrowExPrice;\n }\n\n // extracting total debt\n temp2_ = (vaultVariables_ >> 146) & X64;\n temp2_ = ((temp2_ >> 8) << (temp2_ & X8));\n\n if ((temp2_ / 1e9) > currentData_.debtRemaining) {\n // if liquidation amount is less than 1e9 of total debt then revert\n // so if total debt is $1B then minimum liquidation limit = $1\n // so if total debt is $1T then minimum liquidation limit = $1000\n // partials precision is slightlty above 1e9 so this will make sure that on every liquidation atleast 1 partial gets liquidated\n // not sure if it can result in any issue but restricting amount further more to remove very low amount scenarios totally\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidationAmt);\n }\n\n if (absorb_) {\n temp_ = absorbedLiquidity;\n // temp2_ -> absorbed col\n temp2_ = (temp_ >> 128) & X128;\n // temp_ -> absorbed debt\n temp_ = temp_ & X128;\n\n if (temp_ > currentData_.debtRemaining) {\n // Removing collateral in equal proportion as debt\n currentData_.totalColLiq = ((temp2_ * currentData_.debtRemaining) / temp_);\n temp2_ -= currentData_.totalColLiq;\n // Removing debt\n currentData_.totalDebtLiq = currentData_.debtRemaining;\n unchecked {\n temp_ -= currentData_.debtRemaining;\n }\n currentData_.debtRemaining = 0;\n\n // updating on storage\n absorbedLiquidity = temp_ | (temp2_ << 128);\n } else {\n // updating on storage\n absorbedLiquidity = 0;\n unchecked {\n currentData_.debtRemaining -= temp_;\n }\n currentData_.totalDebtLiq = temp_;\n currentData_.totalColLiq = temp2_;\n }\n }\n\n // current tick should be greater than liquidationTick and it cannot be greater than maxTick as absorb will run\n if (currentData_.tick > memoryVars_.liquidationTick) {\n if (currentData_.debtRemaining > 0) {\n // Stores liquidated debt & collateral in each loop\n uint debtLiquidated_;\n uint colLiquidated_;\n uint debtFactor_ = BigMathVault.TWO_POWER_64;\n\n TickHasDebt memory tickHasDebt_;\n unchecked {\n tickHasDebt_.mapId = (currentData_.tick < 0)\n ? (((currentData_.tick + 1) / 256) - 1)\n : (currentData_.tick / 256);\n }\n\n tickInfo_.ratio = TickMath.getRatioAtTick(tickInfo_.tick);\n\n if (currentData_.tickStatus == 1) {\n // top tick is not liquidated. Hence it's a perfect tick.\n currentData_.ratio = tickInfo_.ratio;\n // if current tick in liquidation is a perfect tick then it is also the next tick that has debt.\n tickHasDebt_.nextTick = currentData_.tick;\n } else {\n // top tick is liquidated. Hence it has partials.\n // next tick that has debt liquidity will have to be fetched from tickHasDebt\n unchecked {\n tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n tickInfo_.partials = (branch_.data >> 22) & X30;\n currentData_.ratio = tickInfo_.ratioOneLess + ((tickInfo_.length * tickInfo_.partials) / X30);\n \n if ((memoryVars_.liquidationTick + 1) == tickInfo_.tick && (tickInfo_.partials == 1)) {\n if (to_ == 0x000000000000000000000000000000000000dEaD) {\n // revert with liquidated amounts if to_ address is the dead address.\n // this can be used in a resolver to find the max liquidatable amounts.\n revert FluidLiquidateResult(0, 0);\n }\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidation);\n }\n }\n }\n\n while (true) {\n if (currentData_.tickStatus == 1) {\n // not liquidated -> Getting the debt from tick data itself\n temp2_ = tickData[currentData_.tick];\n // temp_ => tick debt\n temp_ = (temp2_ >> 25) & X64;\n // Converting big number into normal number\n temp_ = (temp_ >> 8) << (temp_ & X8);\n // Updating tickData on storage with removing debt & adding connection to branch\n tickData[currentData_.tick] =\n 1 | // set tick as liquidated\n (temp2_ & 0x1fffffe) | // set same total tick ids\n (branch_.id << 26) | // branch id where this tick got liquidated\n (branch_.debtFactor << 56);\n } else {\n // already liquidated -> Get the debt from branch data in big number\n // temp_ => tick debt\n temp_ = (branch_.data >> 52) & X64;\n // Converting big number into normal number\n temp_ = (temp_ >> 8) << (temp_ & X8);\n // Branch is getting updated over the end\n }\n\n // Adding new debt into active debt for liquidation\n currentData_.debt += temp_;\n\n // Adding new col into active col for liquidation\n // Ratio is in 2**96 decimals hence multiplying debt with 2**96 to get proper collateral\n currentData_.col += (temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / currentData_.ratio;\n\n if (\n (tickHasDebt_.nextTick == currentData_.tick && currentData_.tickStatus == 1) ||\n tickHasDebt_.tickHasDebt == 0\n ) {\n // Fetching next perfect tick with liquidity\n // tickHasDebt_.tickHasDebt == 0 will only happen in the first while loop\n // in the very first perfect tick liquidation it'll be 0\n if (tickHasDebt_.tickHasDebt == 0) {\n tickHasDebt_.tickHasDebt = tickHasDebt[tickHasDebt_.mapId];\n }\n\n // in 1st loop tickStatus can be 2. Meaning not a perfect current tick\n if (currentData_.tickStatus == 1) {\n unchecked {\n tickHasDebt_.bitsToRemove = uint(-currentData_.tick + (tickHasDebt_.mapId * 256 + 256));\n }\n // Removing current top tick from tickHasDebt\n tickHasDebt_.tickHasDebt =\n (tickHasDebt_.tickHasDebt << tickHasDebt_.bitsToRemove) >>\n tickHasDebt_.bitsToRemove;\n // Updating in storage if tickHasDebt becomes 0.\n if (tickHasDebt_.tickHasDebt == 0) {\n tickHasDebt[tickHasDebt_.mapId] = 0;\n }\n }\n\n // For last user remaining in vault there could be a lot of while loop.\n // Chances of this to happen is extremely low (like ~0%)\n while (true) {\n if (tickHasDebt_.tickHasDebt > 0) {\n unchecked {\n tickHasDebt_.nextTick =\n tickHasDebt_.mapId *\n 256 +\n int(tickHasDebt_.tickHasDebt.mostSignificantBit()) -\n 1;\n }\n break;\n }\n\n // tickHasDebt_.tickHasDebt == 0. Checking if minimum tick of this mapID is less than liquidationTick_\n // if true that means now the next tick is not needed as liquidation gets over minimum at liquidationTick_\n unchecked {\n if ((tickHasDebt_.mapId * 256) < memoryVars_.liquidationTick) {\n tickHasDebt_.nextTick = type(int).min;\n break;\n }\n\n // Fetching next tick has debt by decreasing tickHasDebt_.mapId first\n tickHasDebt_.tickHasDebt = tickHasDebt[--tickHasDebt_.mapId];\n }\n }\n }\n\n // Fetching refTick. refTick is the biggest tick of these 3:\n // 1. Next tick with liquidity (from tickHasDebt)\n // 2. Minima tick of current branch\n // 3. Liquidation threshold tick\n {\n // Setting currentData_.refTick & currentData_.refTickStatus\n if (\n branch_.minimaTick > tickHasDebt_.nextTick &&\n branch_.minimaTick > memoryVars_.liquidationTick\n ) {\n // next tick will be of base branch (merge)\n currentData_.refTick = branch_.minimaTick;\n currentData_.refTickStatus = 2;\n } else if (tickHasDebt_.nextTick > memoryVars_.liquidationTick) {\n // next tick will be next tick from perfect tick\n currentData_.refTick = tickHasDebt_.nextTick;\n currentData_.refTickStatus = 1;\n } else {\n // next tick is threshold tick\n currentData_.refTick = memoryVars_.liquidationTick;\n currentData_.refTickStatus = 3; // leads to end of liquidation loop\n }\n }\n\n // using tickInfo variable again for ref tick as we don't have the need for it any more\n tickInfo_.ratio = TickMath.getRatioAtTick(int24(currentData_.refTick));\n if (currentData_.refTickStatus == 2) {\n // merge current branch with base branch\n unchecked {\n tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n // Fetching base branch data to get the base branch's partial\n branch_.baseBranchData = branchData[((branch_.data >> 166) & X30)];\n tickInfo_.partials = (branch_.baseBranchData >> 22) & X30;\n tickInfo_.currentRatio =\n tickInfo_.ratioOneLess +\n ((tickInfo_.length * tickInfo_.partials) / X30);\n currentData_.refRatio = tickInfo_.currentRatio;\n }\n } else {\n // refTickStatus can only be 1 (next tick from perfect tick) or 3 (liquidation threshold tick)\n tickInfo_.currentRatio = tickInfo_.ratio;\n currentData_.refRatio = tickInfo_.ratio;\n tickInfo_.partials = X30;\n }\n\n // Formula: (debt_ - x) / (col_ - (x * colPerDebt_)) = ratioEnd_\n // x = ((ratioEnd_ * col) - debt_) / ((colPerDebt_ * ratioEnd_) - 1)\n // x is debtToLiquidate_\n // col_ = debt_ / ratioStart_ -> (currentData_.debt / currentData_.ratio)\n // ratioEnd_ is currentData_.refRatio\n //\n // Calculation results of numerator & denominator is always negative\n // which will cancel out to give positive output in the end so we can safely cast to uint.\n // for nominator:\n // ratioStart can only be >= ratioEnd so first part can only be reducing currentData_.debt leading to\n // currentData_.debt reduced - currentData_.debt original * 1e27 -> can only be a negative number\n // for denominator:\n // currentData_.colPerDebt and currentData_.refRatio are inversely proportional to each other.\n // the maximum value they can ever be is ~9.97e26 which is the 0.3% away from 100% because liquidation\n // threshold + liquidation penalty can never be > 99.7%. This can also be verified by going back from\n // min / max ratio values further up where we fetch oracle price etc.\n // as optimization we can inverse nominator and denominator subtraction to directly get a positive number.\n\n debtLiquidated_ =\n // nominator\n ((currentData_.debt - (currentData_.refRatio * currentData_.debt) / currentData_.ratio) *\n 1e27) /\n // denominator\n (1e27 - ((currentData_.colPerDebt * currentData_.refRatio) / TickMath.ZERO_TICK_SCALED_RATIO));\n\n colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27;\n\n if (currentData_.debt == debtLiquidated_) {\n debtLiquidated_ -= 1;\n }\n\n if (debtLiquidated_ >= currentData_.debtRemaining || currentData_.refTickStatus == 3) {\n // End of liquidation as full amount to liquidate or liquidation threshold tick has been reached;\n\n // Updating tickHasDebt on storage.\n tickHasDebt[tickHasDebt_.mapId] = tickHasDebt_.tickHasDebt;\n\n if (debtLiquidated_ >= currentData_.debtRemaining) {\n // Liquidation ended between currentTick & refTick.\n // Not all of liquidatable debt is actually liquidated -> recalculate\n debtLiquidated_ = currentData_.debtRemaining;\n colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27;\n // Liquidating to debt. temp_ => final ratio after liquidation\n // liquidatable debt - debtLiquidated / liquidatable col - colLiquidated\n temp_ =\n ((currentData_.debt - debtLiquidated_) * TickMath.ZERO_TICK_SCALED_RATIO) /\n (currentData_.col - colLiquidated_);\n // Fetching tick of where liquidation ended\n (tickInfo_.tick, tickInfo_.ratioOneLess) = TickMath.getTickAtRatio(temp_);\n if ((tickInfo_.tick < currentData_.refTick) && (tickInfo_.partials == X30)) {\n // this situation might never happen\n // if this happens then there might be some very edge case precision of few weis which is returning 1 tick less\n // if the above were to ever happen then tickInfo_.tick only be currentData_.refTick - 1\n // in this case the partial will be very very near to full (X30)\n // increasing tick by 2 and making partial as 1 which is basically very very near to currentData_.refTick\n unchecked {\n tickInfo_.tick += 2;\n }\n tickInfo_.partials = 1;\n } else {\n unchecked {\n // Increasing tick by 1 as final ratio will probably be a partial\n ++tickInfo_.tick;\n\n // if ref tick is old liquidated tick then storing partials in temp2_\n // tickInfo_.partials contains partial of branch which is the current ref tick\n temp2_ = (currentData_.refTickStatus == 2 && tickInfo_.tick == currentData_.refTick) ? tickInfo_.partials : 0;\n\n tickInfo_.ratio = (tickInfo_.ratioOneLess * 10015) / 10000;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n tickInfo_.partials = ((temp_ - tickInfo_.ratioOneLess) * X30) / tickInfo_.length;\n\n // Taking edge cases where partial comes as 0 or X30 meaning perfect tick.\n // Hence, increasing or reducing it by 1 as liquidation tick cannot be perfect tick.\n tickInfo_.partials = tickInfo_.partials == 0 ? 1 : tickInfo_.partials >= X30\n ? X30 - 1\n : tickInfo_.partials;\n }\n if (temp2_ > 0 && temp2_ >= tickInfo_.partials) {\n // if refTick is liquidated tick and hence contains partials then checking that\n // current liquidation tick's partial should not be less than last liquidation refTick\n\n // not sure if this is even possible to happen but adding checks to avoid it fully\n // if it reverts here then next liquidation on next block should go through fine\n revert FluidVaultError(ErrorTypes.VaultT1__LiquidationReverts);\n }\n }\n } else {\n // End in liquidation threshold.\n // finalRatio_ = currentData_.refRatio;\n // Increasing liquidation threshold tick by 1 partial. With 1 partial it'll reach to the next tick.\n // Ratio change will be negligible. Doing this as liquidation threshold tick can also be a perfect non-liquidated tick.\n unchecked {\n tickInfo_.tick = currentData_.refTick + 1;\n }\n // Making partial as 1 so it doesn't stay perfect tick\n tickInfo_.partials = 1;\n // length is not needed as only partials are written to storage\n }\n\n // debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt\n // -> debtFactor * leftOverDebt / liquidatableDebt\n debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt;\n currentData_.totalDebtLiq += debtLiquidated_;\n currentData_.debt -= debtLiquidated_; // currentData_.debt => leftOverDebt after debtLiquidated_\n currentData_.totalColLiq += colLiquidated_;\n currentData_.col -= colLiquidated_; // currentData_.col => leftOverCol after colLiquidated_\n\n // Updating branch's debt factor & write to storage as liquidation is over\n branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_);\n\n if (currentData_.debt < 100) {\n // this can happen when someone tries to create a dust tick\n revert FluidVaultError(ErrorTypes.VaultT1__BranchDebtTooLow);\n }\n\n unchecked {\n // Tick to insert\n temp2_ = tickInfo_.tick < 0\n ? (uint(-tickInfo_.tick) << 1)\n : ((uint(tickInfo_.tick) << 1) | 1);\n }\n\n // Updating Branch data with debt factor, debt, partials, minima tick & assigning is liquidated\n branchData[branch_.id] =\n ((branch_.data >> 166) << 166) |\n 1 | // set as liquidated\n (temp2_ << 2) | // minima tick of branch\n (tickInfo_.partials << 22) |\n (currentData_.debt.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52) | // branch debt\n (branch_.debtFactor << 116);\n\n // Updating vault variables with current branch & tick\n vaultVariables_ =\n ((vaultVariables_ >> 52) << 52) |\n 2 | // set as liquidated\n (temp2_ << 2) | // top tick\n (branch_.id << 22);\n break;\n }\n\n unchecked {\n // debtLiquidated_ >= currentData_.debtRemaining leads to loop break in if statement above\n // so this can be unchecked\n currentData_.debtRemaining -= debtLiquidated_;\n }\n\n // debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt\n // -> debtFactor * leftOverDebt / liquidatableDebt\n debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt;\n currentData_.totalDebtLiq += debtLiquidated_;\n currentData_.debt -= debtLiquidated_;\n currentData_.totalColLiq += colLiquidated_;\n currentData_.col -= colLiquidated_;\n\n // updating branch's debt factor\n branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_);\n // Setting debt factor as 1 << 64 again\n debtFactor_ = BigMathVault.TWO_POWER_64;\n\n if (currentData_.refTickStatus == 2) {\n // ref tick is base branch's minima hence merging current branch to base branch\n // and making base branch as current branch.\n\n // read base branch related data\n temp_ = (branch_.data >> 166) & X30; // temp_ -> base branch id\n temp2_ = branch_.baseBranchData;\n {\n uint newBranchDebtFactor_ = (temp2_ >> 116) & X50;\n\n // connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor\n uint connectionFactor_ = newBranchDebtFactor_.divBigNumber(branch_.debtFactor);\n // Updating current branch in storage\n branchData[branch_.id] =\n ((branch_.data >> 166) << 166) | // deleting debt / partials / minima tick\n 2 | // setting as merged\n (connectionFactor_ << 116); // set new connectionFactor\n\n // Storing base branch in memory\n // Updating branch ID to base branch ID\n branch_.id = temp_;\n // Updating branch data with base branch data\n branch_.data = temp2_;\n // Remove next branch connection from base branch\n branch_.debtFactor = newBranchDebtFactor_;\n // temp_ => minima tick of base branch\n temp_ = (temp2_ >> 196) & X20;\n if (temp_ > 0) {\n unchecked {\n branch_.minimaTick = (temp_ & 1) == 1\n ? int256((temp_ >> 1) & X19)\n : -int256((temp_ >> 1) & X19);\n }\n } else {\n branch_.minimaTick = type(int).min;\n }\n }\n }\n\n // Making refTick as currentTick\n currentData_.tick = currentData_.refTick;\n currentData_.tickStatus = currentData_.refTickStatus;\n currentData_.ratio = currentData_.refRatio;\n }\n }\n }\n\n // calculating net token amounts using exchange price\n actualDebtAmt_ = (currentData_.totalDebtLiq * memoryVars_.borrowExPrice) / EXCHANGE_PRICES_PRECISION;\n actualColAmt_ = (currentData_.totalColLiq * memoryVars_.supplyExPrice) / EXCHANGE_PRICES_PRECISION;\n\n // Chances of this to happen are in few wei\n if (actualDebtAmt_ > debtAmt_) {\n // calc new actualColAmt_ via ratio.\n actualColAmt_ = actualColAmt_ * (debtAmt_ / actualDebtAmt_);\n actualDebtAmt_ = debtAmt_;\n }\n\n if (actualDebtAmt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidation);\n }\n\n if (((actualColAmt_ * 1e18) / actualDebtAmt_) < colPerUnitDebt_) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessSlippageLiquidation);\n }\n\n if (to_ == 0x000000000000000000000000000000000000dEaD) {\n // revert with liquidated amounts if to_ address is the dead address.\n // this can be used in a resolver to find the max liquidatable amounts.\n revert FluidLiquidateResult(actualColAmt_, actualDebtAmt_);\n }\n\n // payback at Liquidity\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n temp_ = actualDebtAmt_;\n if (actualDebtAmt_ < msg.value) {\n unchecked {\n // subtraction can be unchecked because of if check above\n SafeTransfer.safeTransferNative(msg.sender, msg.value - actualDebtAmt_);\n }\n }\n // else if actualDebtAmt_ > msg.value not possible as actualDebtAmt_ can maximally be debtAmt_ and\n // msg.value == debtAmt_ is checked in the beginning of function.\n } else {\n temp_ = 0;\n }\n unchecked {\n // payback at liquidity\n LIQUIDITY.operate{ value: temp_ }(\n BORROW_TOKEN,\n 0,\n -int(actualDebtAmt_),\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n // withdraw at liquidity\n LIQUIDITY.operate(SUPPLY_TOKEN, -int(actualColAmt_), 0, to_, address(0), new bytes(0));\n }\n\n // Calculating new total collateral & total debt.\n // temp_ -> total supply\n temp_ = (vaultVariables_ >> 82) & X64;\n temp_ = ((temp_ >> 8) << (temp_ & X8)) - currentData_.totalColLiq;\n // temp2_ -> total borrow\n temp2_ = (vaultVariables_ >> 146) & X64;\n temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) - currentData_.totalDebtLiq;\n // Updating vault variables on storage\n // Converting total supply & total borrow in 64 bits (56 | 8) bignumber\n vaultVariables =\n (vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply\n (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow\n\n emit LogLiquidate(msg.sender, actualColAmt_, actualDebtAmt_, to_);\n }\n\n /// @dev Checks total supply of vault's in Liquidity Layer & Vault contract and rebalance it accordingly\n /// if vault supply is more than Liquidity Layer then deposit difference through reserve/rebalance contract\n /// if vault supply is less than Liquidity Layer then withdraw difference to reserve/rebalance contract\n /// if vault borrow is more than Liquidity Layer then borrow difference to reserve/rebalance contract\n /// if vault borrow is less than Liquidity Layer then payback difference through reserve/rebalance contract\n function rebalance() external payable returns (int supplyAmt_, int borrowAmt_) {\n (supplyAmt_, borrowAmt_) = abi.decode(_spell(SECONDARY_IMPLEMENTATION, msg.data), (int, int));\n }\n\n /// @dev liquidity callback for cheaper token transfers in case of deposit or payback.\n /// only callable by Liquidity during an operation.\n function liquidityCallback(address token_, uint amount_, bytes calldata data_) external {\n if (msg.sender != address(LIQUIDITY))\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidityCallbackAddress);\n if (vaultVariables & 1 == 0) revert FluidVaultError(ErrorTypes.VaultT1__NotEntered);\n\n SafeTransfer.safeTransferFrom(token_, abi.decode(data_, (address)), address(LIQUIDITY), amount_);\n }\n\n constructor(ConstantViews memory constants_) Helpers(constants_) {\n // Note that vaults are deployed by VaultFactory so we somewhat trust the values being passed in\n\n // Setting branch in vault.\n vaultVariables = (vaultVariables) | (1 << 22) | (1 << 52);\n\n uint liqSupplyExchangePrice_ = (LIQUIDITY.readFromStorage(LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT) >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64;\n uint liqBorrowExchangePrice_ = (LIQUIDITY.readFromStorage(LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT) >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64;\n\n if (\n liqSupplyExchangePrice_ < EXCHANGE_PRICES_PRECISION || liqBorrowExchangePrice_ < EXCHANGE_PRICES_PRECISION\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__TokenNotInitialized);\n }\n // Updating initial rates in storage\n rates =\n liqSupplyExchangePrice_ |\n (liqBorrowExchangePrice_ << 64) |\n (EXCHANGE_PRICES_PRECISION << 128) |\n (EXCHANGE_PRICES_PRECISION << 192);\n }\n\n fallback() external {\n if (!(VAULT_FACTORY.isGlobalAuth(msg.sender) || VAULT_FACTORY.isVaultAuth(address(this), msg.sender))) {\n revert FluidVaultError(ErrorTypes.VaultT1__NotAnAuth);\n }\n\n // Delegate the current call to `implementation`.\n // This does not return to its internall call site, it will return directly to the external caller.\n // solhint-disable-next-line no-inline-assembly\n _spell(ADMIN_IMPLEMENTATION, msg.data);\n }\n\n function _spell(address target_, bytes memory data_) private returns (bytes memory response_) {\n assembly {\n let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)\n let size := returndatasize()\n\n response_ := mload(0x40)\n mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))\n mstore(response_, size)\n returndatacopy(add(response_, 0x20), 0, size)\n\n switch iszero(succeeded)\n case 1 {\n // throw if delegatecall failed\n returndatacopy(0x00, 0x00, size)\n revert(0x00, size)\n }\n }\n }\n}\n" }, "contracts/protocols/vault/vaultT1/coreModule/main2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"../common/variables.sol\";\nimport { IFluidOracle } from \"../../../../oracle/fluidOracle.sol\";\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { Error } from \"../../error.sol\";\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { IFluidVaultT1 } from \"../../interfaces/iVaultT1.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { Events } from \"./events.sol\";\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\nimport { LiquidityCalcs } from \"../../../../libraries/liquidityCalcs.sol\";\nimport { IFluidLiquidity } from \"../../../../liquidity/interfaces/iLiquidity.sol\";\nimport { SafeTransfer } from \"../../../../libraries/safeTransfer.sol\";\n\n/// @notice Fluid Vault protocol secondary methods contract.\n/// Implements `absorb()` and `rebalance()` methods, extracted from main contract due to contract size limits.\n/// Methods are limited to be called via delegateCall only (as done by Vault CoreModule \"VaultT1\" contract).\ncontract FluidVaultT1Secondary is Variables, Error, Structs, Events {\n using BigMathMinified for uint;\n\n address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // 30 bits (used for partials mainly)\n uint internal constant X8 = 0xff;\n uint internal constant X10 = 0x3ff;\n uint internal constant X16 = 0xffff;\n uint internal constant X19 = 0x7ffff;\n uint internal constant X20 = 0xfffff;\n uint internal constant X24 = 0xffffff;\n uint internal constant X25 = 0x1ffffff;\n uint internal constant X30 = 0x3fffffff;\n uint internal constant X35 = 0x7ffffffff;\n uint internal constant X50 = 0x3ffffffffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n uint internal constant X96 = 0xffffffffffffffffffffffff;\n uint internal constant X128 = 0xffffffffffffffffffffffffffffffff;\n\n address private immutable addressThis;\n\n constructor() {\n addressThis = address(this);\n }\n\n modifier _verifyCaller() {\n if (address(this) == addressThis) {\n revert FluidVaultError(ErrorTypes.VaultT1__OnlyDelegateCallAllowed);\n }\n _;\n }\n\n /// @dev absorb function absorbs the bad debt if the bad debt is above max limit. The main use of it is\n /// if the bad debt didn't got liquidated in time maybe due to sudden price drop or bad debt was extremely small to liquidate\n /// and the bad debt goes above 100% ratio then there's no incentive for anyone to liquidate now\n /// hence absorb functions absorbs that bad debt to allow newer bad debt to liquidate seamlessly.\n /// if absorbing were to happen after this it's on governance on how to deal with it\n /// although it can still be removed through liquidate via liquidator if the price goes back up and liquidation becomes beneficial\n /// upon absorbed user position gets 100% liquidated.\n function absorb(uint vaultVariables_, int maxTick_) public _verifyCaller returns (uint) {\n AbsorbMemoryVariables memory a_;\n\n // Temporary holder variables, used many times for different small few liner things\n uint temp_;\n uint temp2_;\n\n TickHasDebt memory tickHasDebt_;\n\n {\n // liquidating ticks above max ratio\n\n // temp_ -> top tick\n temp_ = ((vaultVariables_ >> 2) & X20);\n // increasing startingTick_ by 1 so the current tick comes into looping equation\n a_.startingTick = (temp_ & 1) == 1 ? (int(temp_ >> 1) + 1) : (-int(temp_ >> 1) + 1);\n\n tickHasDebt_.mapId = a_.startingTick < 0 ? ((a_.startingTick + 1) / 256) - 1 : a_.startingTick / 256;\n\n tickHasDebt_.tickHasDebt = tickHasDebt[tickHasDebt_.mapId];\n\n {\n // For last user remaining in vault there could be a lot of while loop.\n // Chances of this to happen is extremely low (like ~0%)\n tickHasDebt_.nextTick = TickMath.MAX_TICK;\n while (true) {\n if (tickHasDebt_.tickHasDebt > 0) {\n a_.mostSigBit = tickHasDebt_.tickHasDebt.mostSignificantBit();\n tickHasDebt_.nextTick = tickHasDebt_.mapId * 256 + int(a_.mostSigBit) - 1;\n\n while (tickHasDebt_.nextTick > maxTick_) {\n // storing tickData into temp_\n temp_ = tickData[tickHasDebt_.nextTick];\n // temp2_ -> tick's debt\n temp2_ = (temp_ >> 25) & X64;\n // converting big number into normal number\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n // Absorbing tick's debt & collateral\n a_.debtAbsorbed += temp2_;\n // calculating collateral from debt & ratio and adding to a_.colAbsorbed\n a_.colAbsorbed += ((temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) /\n TickMath.getRatioAtTick(int24(tickHasDebt_.nextTick)));\n // Update tick data on storage. Making tick as 100% liquidated\n tickData[tickHasDebt_.nextTick] = 1 | (temp_ & 0x1fffffe) | (1 << 25); // set as 100% liquidated\n\n // temp_ = bits to remove\n temp_ = 257 - a_.mostSigBit;\n tickHasDebt_.tickHasDebt = (tickHasDebt_.tickHasDebt << temp_) >> temp_;\n if (tickHasDebt_.tickHasDebt == 0) break;\n\n a_.mostSigBit = tickHasDebt_.tickHasDebt.mostSignificantBit();\n tickHasDebt_.nextTick = tickHasDebt_.mapId * 256 + int(a_.mostSigBit) - 1;\n }\n // updating tickHasDebt on storage\n tickHasDebt[tickHasDebt_.mapId] = tickHasDebt_.tickHasDebt;\n }\n\n // tickHasDebt_.tickHasDebt == 0 from here.\n\n if (tickHasDebt_.nextTick <= maxTick_) {\n break;\n }\n\n if (tickHasDebt_.mapId < -129) {\n tickHasDebt_.nextTick = type(int).min;\n break;\n }\n\n // Fetching next tickHasDebt by decreasing tickHasDebt_.mapId first\n tickHasDebt_.tickHasDebt = tickHasDebt[--tickHasDebt_.mapId];\n }\n }\n }\n\n // After the above loop we will get nextTick stored in tickHasDebt_ which we will use to compare & set things in the end\n\n {\n TickData memory tickInfo_;\n BranchData memory branch_;\n // if this remains 0 that means create a new branch over the end\n uint newBranchId_;\n\n {\n // Liquidate branches in a loop and store the end branch\n branch_.id = (vaultVariables_ >> 22) & X30;\n branch_.data = branchData[branch_.id];\n // Checking if current branch is liquidated\n if ((vaultVariables_ & 2) == 0) {\n // current branch is not liquidated hence it can be used as a new branch if needed\n newBranchId_ = branch_.id;\n\n // Checking the base branch minima tick. temp_ = base branch minima tick\n temp_ = (branch_.data >> 196) & X20;\n if (temp_ > 0) {\n // Setting the base branch as current liquidatable branch\n branch_.id = (branch_.data >> 166) & X30;\n branch_.data = branchData[branch_.id];\n branch_.minimaTick = (temp_ & 1) == 1 ? int(temp_ >> 1) : -int(temp_ >> 1);\n } else {\n // the current branch is base branch, hence need to setup a new base branch\n branch_.id = 0;\n branch_.data = 0;\n branch_.minimaTick = type(int).min;\n }\n } else {\n // current branch is liquidated\n temp_ = (branch_.data >> 2) & X20;\n branch_.minimaTick = (temp_ & 1) == 1 ? int(temp_ >> 1) : -int(temp_ >> 1);\n }\n while (branch_.minimaTick > maxTick_) {\n // Check base branch, if exists then check if minima tick is above max tick then liquidate it.\n tickInfo_.ratio = TickMath.getRatioAtTick(int24(branch_.minimaTick));\n tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n\n // partials\n tickInfo_.partials = (branch_.data >> 22) & X30;\n\n tickInfo_.currentRatio = tickInfo_.ratioOneLess + ((tickInfo_.length * tickInfo_.partials) / X30);\n\n // debt in branch\n temp2_ = (branch_.data >> 52) & X64;\n // converting big number into normal number\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n // Absorbing branch's debt & collateral\n a_.debtAbsorbed += temp2_;\n // calculating branch's collateral using debt & ratio and adding it to a_.colAbsorbed\n a_.colAbsorbed += (temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) / tickInfo_.currentRatio;\n\n // Closing branch\n branchData[branch_.id] = branch_.data | 3;\n\n // Setting new branch\n temp_ = (branch_.data >> 196) & X20; // temp_ -> minima tick of connected branch\n if (temp_ > 0) {\n // Setting the base branch as current liquidatable branch\n branch_.id = (branch_.data >> 166) & X30;\n branch_.data = branchData[branch_.id];\n branch_.minimaTick = (temp_ & 1) == 1 ? int(temp_ >> 1) : -int(temp_ >> 1);\n } else {\n // the current branch is base branch, hence need to setup a new base branch\n branch_.id = 0;\n branch_.data = 0;\n branch_.minimaTick = type(int).min;\n }\n }\n }\n\n if (tickHasDebt_.nextTick >= branch_.minimaTick) {\n // new top tick is not liquidated\n // temp2_ = tick to insert\n if (tickHasDebt_.nextTick > type(int).min) {\n temp2_ = tickHasDebt_.nextTick < 0\n ? (uint(-tickHasDebt_.nextTick) << 1)\n : ((uint(tickHasDebt_.nextTick) << 1) | 1);\n } else {\n temp2_ = 0;\n }\n if (newBranchId_ == 0) {\n // initializing a new branch\n // newBranchId_ = total current branches + 1\n unchecked {\n newBranchId_ = ((vaultVariables_ >> 52) & X30) + 1;\n }\n vaultVariables_ =\n ((vaultVariables_ >> 82) << 82) |\n (temp2_ << 2) |\n (newBranchId_ << 22) |\n (newBranchId_ << 52);\n } else {\n // using already initialized non liquidated branch\n vaultVariables_ = ((vaultVariables_ >> 22) << 22) | (temp2_ << 2);\n }\n\n if (branch_.minimaTick > type(int).min) {\n temp2_ = branch_.minimaTick < 0\n ? (uint(-branch_.minimaTick) << 1)\n : ((uint(branch_.minimaTick) << 1) | 1);\n // set base branch id and minima tick\n branchData[newBranchId_] = (branch_.id << 166) | (temp2_ << 196);\n } else {\n // new base branch does not have any connected branch\n branchData[newBranchId_] = 0;\n }\n } else {\n // new top tick is liquidated\n temp2_ = branch_.minimaTick < 0\n ? (uint(-branch_.minimaTick) << 1)\n : ((uint(branch_.minimaTick) << 1) | 1);\n if (newBranchId_ == 0) {\n vaultVariables_ = ((vaultVariables_ >> 52) << 52) | 2 | (temp2_ << 2) | (branch_.id << 22);\n } else {\n // uninitializing the non liquidated branch\n vaultVariables_ =\n ((vaultVariables_ >> 82) << 82) |\n 2 |\n (temp2_ << 2) |\n (branch_.id << 22) |\n ((newBranchId_ - 1) << 52); // decreasing total branch by 1\n branchData[newBranchId_] = 0;\n }\n }\n }\n\n // updating absorbed liquidity on storage\n absorbedLiquidity = absorbedLiquidity + a_.debtAbsorbed + (a_.colAbsorbed << 128);\n\n emit LogAbsorb(a_.colAbsorbed, a_.debtAbsorbed);\n\n // returning updated vault variables\n return vaultVariables_;\n }\n\n /// @dev Checks total supply of vault's in Liquidity Layer & Vault contract and rebalance it accordingly\n /// if vault supply is more than Liquidity Layer then deposit difference through reserve/rebalance contract\n /// if vault supply is less than Liquidity Layer then withdraw difference to reserve/rebalance contract\n /// if vault borrow is more than Liquidity Layer then borrow difference to reserve/rebalance contract\n /// if vault borrow is less than Liquidity Layer then payback difference through reserve/rebalance contract\n function rebalance() external payable _verifyCaller returns (int supplyAmt_, int borrowAmt_) {\n if (msg.sender != rebalancer) {\n revert FluidVaultError(ErrorTypes.VaultT1__NotRebalancer);\n }\n\n uint vaultVariables_ = vaultVariables;\n // ############# turning re-entrancy bit on #############\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n IFluidVaultT1.ConstantViews memory constants_ = IFluidVaultT1(address(this)).constantsView();\n\n if (msg.value > 0 && !(constants_.supplyToken == NATIVE_TOKEN || constants_.borrowToken == NATIVE_TOKEN)) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueInRebalance);\n }\n\n IFluidLiquidity liquidity_ = IFluidLiquidity(constants_.liquidity);\n RebalanceMemoryVariables memory r_;\n\n (r_.liqSupplyExPrice, r_.liqBorrowExPrice, r_.vaultSupplyExPrice, r_.vaultBorrowExPrice) = IFluidVaultT1(\n address(this)\n ).updateExchangePrices(vaultVariables2);\n\n // extract vault supply at Liquidity -> 64 bits starting from bit 1 (first bit is interest mode)\n uint totalSupplyLiquidity_ = (liquidity_.readFromStorage(constants_.liquidityUserSupplySlot) >>\n LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n totalSupplyLiquidity_ = (totalSupplyLiquidity_ >> 8) << (totalSupplyLiquidity_ & X8);\n totalSupplyLiquidity_ =\n (totalSupplyLiquidity_ * r_.liqSupplyExPrice) /\n LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n\n // extract vault borrowings at Liquidity -> 64 bits starting from bit 1 (first bit is interest mode)\n uint totalBorrowLiquidity_ = (liquidity_.readFromStorage(constants_.liquidityUserBorrowSlot) >>\n LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64;\n totalBorrowLiquidity_ = (totalBorrowLiquidity_ >> 8) << (totalBorrowLiquidity_ & X8);\n totalBorrowLiquidity_ =\n (totalBorrowLiquidity_ * r_.liqBorrowExPrice) /\n LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n\n uint totalSupplyVault_ = (vaultVariables_ >> 82) & X64;\n totalSupplyVault_ = (totalSupplyVault_ >> 8) << (totalSupplyVault_ & X8);\n totalSupplyVault_ = (totalSupplyVault_ * r_.vaultSupplyExPrice) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n\n uint totalBorrowVault_ = (vaultVariables_ >> 146) & X64;\n totalBorrowVault_ = (totalBorrowVault_ >> 8) << (totalBorrowVault_ & X8);\n totalBorrowVault_ = (totalBorrowVault_ * r_.vaultBorrowExPrice) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n\n uint value_;\n\n if (totalSupplyVault_ > totalSupplyLiquidity_) {\n // Fetch tokens from revenue/rebalance contract and supply in liquidity contract\n // This is the scenario when the supply rewards are going in vault, hence\n // the vault total supply is increasing at a higher pace than Liquidity contract.\n // We are not transferring rewards right when we set the rewards to keep things clean.\n // Also, this can also happen in case when supply rate magnifier is greater than 1.\n\n supplyAmt_ = int(totalSupplyVault_) - int(totalSupplyLiquidity_);\n\n if (constants_.supplyToken == NATIVE_TOKEN) {\n if (msg.value > uint(supplyAmt_)) {\n value_ = uint(supplyAmt_);\n SafeTransfer.safeTransferNative(msg.sender, msg.value - value_); // sending back excess ETH\n } else {\n value_ = msg.value; // setting amount as msg.value\n }\n supplyAmt_ = int(value_);\n } else {\n value_ = 0;\n }\n\n try liquidity_.operate{ value: value_ }(\n constants_.supplyToken,\n supplyAmt_,\n 0,\n address(0),\n address(0),\n abi.encode(rebalancer)\n ) {\n // if success then do nothing\n } catch {\n supplyAmt_ = 0;\n }\n\n \n } else if (totalSupplyLiquidity_ > totalSupplyVault_) {\n if (constants_.supplyToken == NATIVE_TOKEN && msg.value > 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueInRebalance);\n }\n // Withdraw from Liquidity contract and send it to revenue contract.\n // This is the scenario when the vault user's are getting less ETH APR than what's going on Liquidity contract.\n // When supply rate magnifier is less than 1.\n supplyAmt_ = int(totalSupplyVault_) - int(totalSupplyLiquidity_);\n try liquidity_.operate(constants_.supplyToken, supplyAmt_, 0, rebalancer, address(0), new bytes(0)) {\n // if success then do nothing\n } catch {\n supplyAmt_ = 0;\n }\n }\n\n if (totalBorrowVault_ > totalBorrowLiquidity_) {\n if (constants_.borrowToken == NATIVE_TOKEN && msg.value > 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueInRebalance);\n }\n // Borrow from Liquidity contract and send to revenue/rebalance contract\n // This is the scenario when the vault is charging more borrow to user than the Liquidity contract.\n // When borrow rate magnifier is greater than 1.\n borrowAmt_ = int(totalBorrowVault_) - int(totalBorrowLiquidity_);\n try liquidity_.operate(constants_.borrowToken, 0, borrowAmt_, address(0), rebalancer, new bytes(0)) {\n // if success then do nothing\n } catch {\n borrowAmt_ = 0;\n }\n } else if (totalBorrowLiquidity_ > totalBorrowVault_) {\n // Transfer from revenue/rebalance contract and payback on Liquidity contract\n // This is the scenario when vault protocol is earning rewards so effective borrow rate for users is low.\n // Or the case where borrow rate magnifier is less than 1\n\n borrowAmt_ = int(totalBorrowLiquidity_) - int(totalBorrowVault_);\n\n if (constants_.borrowToken == NATIVE_TOKEN) {\n if (msg.value > uint(borrowAmt_)) {\n value_ = uint(borrowAmt_);\n SafeTransfer.safeTransferNative(msg.sender, msg.value - value_);\n } else {\n value_ = msg.value; // setting amount as msg.value\n }\n borrowAmt_ = int(value_);\n } else {\n value_ = 0;\n }\n\n borrowAmt_ = -borrowAmt_;\n\n try liquidity_.operate{ value: value_ }(\n constants_.borrowToken,\n 0,\n borrowAmt_,\n address(0),\n address(0),\n abi.encode(rebalancer)\n ) {\n // if success then do nothing\n } catch {\n borrowAmt_ = 0;\n }\n }\n\n if (supplyAmt_ == 0 && borrowAmt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__NothingToRebalance);\n }\n\n // Updating vault variable on storage to turn off the reentrancy bit\n vaultVariables = vaultVariables_;\n\n emit LogRebalance(supplyAmt_, borrowAmt_);\n }\n}\n" }, "contracts/protocols/vault/vaultT1/coreModule/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Structs {\n // structs are used to mitigate Stack too deep errors\n\n struct OperateMemoryVars {\n // ## User's position before update ##\n uint oldColRaw;\n uint oldNetDebtRaw; // total debt - dust debt\n int oldTick;\n // ## User's position after update ##\n uint colRaw;\n uint debtRaw;\n uint dustDebtRaw;\n int tick;\n uint tickId;\n // others\n uint256 vaultVariables2;\n uint256 branchId;\n int256 topTick;\n uint liquidityExPrice;\n uint supplyExPrice;\n uint borrowExPrice;\n uint branchData;\n // user's supply slot data in liquidity\n uint userSupplyLiquidityData;\n }\n\n struct BranchData {\n uint id;\n uint data;\n uint ratio;\n uint debtFactor;\n int minimaTick;\n uint baseBranchData;\n }\n\n struct TickData {\n int tick;\n uint data;\n uint ratio;\n uint ratioOneLess;\n uint length;\n uint currentRatio; // current tick is ratio with partials.\n uint partials;\n }\n\n // note: All the below token amounts are in raw form.\n struct CurrentLiquidity {\n uint256 debtRemaining; // Debt remaining to liquidate\n uint256 debt; // Current liquidatable debt before reaching next check point\n uint256 col; // Calculate using debt & ratioCurrent\n uint256 colPerDebt; // How much collateral to liquidate per unit of Debt\n uint256 totalDebtLiq; // Total debt liquidated till now\n uint256 totalColLiq; // Total collateral liquidated till now\n int tick; // Current tick to liquidate\n uint ratio; // Current ratio to liquidate\n uint tickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick\n int refTick; // ref tick to liquidate\n uint refRatio; // ratio at ref tick\n uint refTickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick, if 3 that means it's a liquidation threshold\n }\n\n struct TickHasDebt {\n int tick; // current tick\n int nextTick; // next tick with liquidity\n int mapId; // mapping ID of tickHasDebt\n uint bitsToRemove; // liquidity to remove till tick_ so we can search for next tick\n uint tickHasDebt; // getting tickHasDebt_ from tickHasDebt[mapId_]\n uint mostSigBit; // most significant bit in tickHasDebt_ to get the next tick\n }\n\n struct LiquidateMemoryVars {\n uint256 vaultVariables2;\n int liquidationTick;\n int maxTick;\n uint256 supplyExPrice;\n uint256 borrowExPrice;\n }\n\n struct AbsorbMemoryVariables {\n uint256 debtAbsorbed;\n uint256 colAbsorbed;\n int256 startingTick;\n uint256 mostSigBit;\n }\n\n struct ConstantViews {\n address liquidity;\n address factory;\n address adminImplementation;\n address secondaryImplementation;\n address supplyToken;\n address borrowToken;\n uint8 supplyDecimals;\n uint8 borrowDecimals;\n uint vaultId;\n bytes32 liquiditySupplyExchangePriceSlot;\n bytes32 liquidityBorrowExchangePriceSlot;\n bytes32 liquidityUserSupplySlot;\n bytes32 liquidityUserBorrowSlot;\n }\n\n struct RebalanceMemoryVariables {\n uint256 liqSupplyExPrice;\n uint256 liqBorrowExPrice;\n uint256 vaultSupplyExPrice;\n uint256 vaultBorrowExPrice;\n }\n}\n" }, "contracts/reserve/auth/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ninterface IFluidReserveContract {\n function updateRebalancer(address rebalancer_, bool isRebalancer_) external;\n}\n\ncontract FluidReserveContractAuthHandler {\n event LogUpdateRebalancer(address indexed rebalancer, bool isRebalancer);\n\n IFluidReserveContract public constant RESERVE = IFluidReserveContract(0x264786EF916af64a1DB19F513F24a3681734ce92);\n address public constant TEAM_MULTISIG = 0x4F6F977aCDD1177DCD81aB83074855EcB9C2D49e;\n\n function updateRebalancer(address rebalancer_, bool isRebalancer_) external {\n require(msg.sender == TEAM_MULTISIG, \"not-team-multisig\");\n RESERVE.updateRebalancer(rebalancer_, isRebalancer_);\n\n emit LogUpdateRebalancer(rebalancer_, isRebalancer_);\n }\n}\n" }, "contracts/reserve/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Error {\n error FluidReserveContractError(uint256 errorId_);\n}\n" }, "contracts/reserve/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | Reserve | \n |__________________________________*/\n\n /// @notice thrown when an unauthorized caller is trying to execute an auth-protected method\n uint256 internal constant ReserveContract__Unauthorized = 90001;\n\n /// @notice thrown when an input address is zero\n uint256 internal constant ReserveContract__AddressZero = 90002;\n\n /// @notice thrown when input arrays has different lenghts\n uint256 internal constant ReserveContract__InvalidInputLenghts = 90003;\n\n /// @notice thrown when renounceOwnership is called\n uint256 internal constant ReserveContract__RenounceOwnershipUnsupported = 90004;\n\n /// @notice thrown when wrong msg.value is at time of rebalancing\n uint256 internal constant ReserveContract__WrongValueSent = 90005;\n}\n" }, "contracts/reserve/events.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Events {\n /// @notice Emitted when an address is added or removed from the auths\n event LogUpdateAuth(address indexed auth, bool isAuth);\n\n /// @notice Emitted when an address is added or removed from the rebalancers\n event LogUpdateRebalancer(address indexed rebalancer, bool isRebalancer);\n\n /// @notice Emitted when a token is approved for use by a protocol\n event LogAllow(address indexed protocol, address indexed token, uint256 newAllowance, uint existingAllowance);\n\n /// @notice Emitted when a token is revoked for use by a protocol\n event LogRevoke(address indexed protocol, address indexed token);\n\n /// @notice Emitted when fToken is rebalanced\n event LogRebalanceFToken(address indexed protocol, uint amount);\n\n /// @notice Emitted when vault is rebalanced\n event LogRebalanceVault(address indexed protocol, int colAmount, int debtAmount);\n\n /// @notice Emitted whenever funds for a certain `token` are transfered to Liquidity\n event LogTransferFunds(address indexed token);\n}\n" }, "contracts/reserve/interfaces/iReserveContract.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../liquidity/interfaces/iLiquidity.sol\";\n\ninterface IFluidReserveContract {\n function isRebalancer(address user) external returns (bool);\n\n function initialize(\n address[] memory _auths,\n address[] memory _rebalancers,\n IFluidLiquidity liquidity_,\n address owner_\n ) external;\n\n function rebalanceFToken(address protocol_) external;\n\n function rebalanceVault(address protocol_) external;\n\n function transferFunds(address token_) external;\n\n function getProtocolTokens(address protocol_) external;\n\n function updateAuth(address auth_, bool isAuth_) external;\n\n function updateRebalancer(address rebalancer_, bool isRebalancer_) external;\n\n function approve(address[] memory protocols_, address[] memory tokens_, uint256[] memory amounts_) external;\n\n function revoke(address[] memory protocols_, address[] memory tokens_) external;\n}\n" }, "contracts/reserve/main.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { UUPSUpgradeable } from \"@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { EnumerableSet } from \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\n\nimport { IFluidLiquidity } from \"../liquidity/interfaces/iLiquidity.sol\";\nimport { IFluidLendingFactory } from \"../protocols/lending/interfaces/iLendingFactory.sol\";\nimport { IFTokenAdmin } from \"../protocols/lending/interfaces/iFToken.sol\";\nimport { IFluidVaultT1 } from \"../protocols/vault/interfaces/iVaultT1.sol\";\nimport { SafeTransfer } from \"../libraries/safeTransfer.sol\";\n\nimport { Variables } from \"./variables.sol\";\nimport { Events } from \"./events.sol\";\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { Error } from \"./error.sol\";\n\nabstract contract ReserveContractAuth is Variables, Error, Events {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n /// @dev validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidReserveContractError(ErrorTypes.ReserveContract__AddressZero);\n }\n _;\n }\n\n /// @notice Checks that the sender is an auth\n modifier onlyAuth() {\n if (!isAuth[msg.sender] && owner() != msg.sender)\n revert FluidReserveContractError(ErrorTypes.ReserveContract__Unauthorized);\n _;\n }\n\n /// @notice Updates an auth's status as an auth\n /// @param auth_ The address to update\n /// @param isAuth_ Whether or not the address should be an auth\n function updateAuth(address auth_, bool isAuth_) external onlyOwner validAddress(auth_) {\n isAuth[auth_] = isAuth_;\n emit LogUpdateAuth(auth_, isAuth_);\n }\n\n /// @notice Updates a rebalancer's status as a rebalancer\n /// @param rebalancer_ The address to update\n /// @param isRebalancer_ Whether or not the address should be a rebalancer\n function updateRebalancer(address rebalancer_, bool isRebalancer_) external onlyAuth validAddress(rebalancer_) {\n isRebalancer[rebalancer_] = isRebalancer_;\n emit LogUpdateRebalancer(rebalancer_, isRebalancer_);\n }\n\n /// @notice Approves protocols to spend the reserves tokens\n /// @dev The parameters are parallel arrays\n /// @param protocols_ The protocols that will be spending reserve tokens\n /// @param tokens_ The tokens to approve\n /// @param amounts_ The amounts to approve\n function approve(\n address[] memory protocols_,\n address[] memory tokens_,\n uint256[] memory amounts_\n ) external onlyAuth {\n if (protocols_.length != tokens_.length || tokens_.length != amounts_.length) {\n revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);\n }\n\n for (uint256 i = 0; i < protocols_.length; i++) {\n address protocol_ = protocols_[i];\n address token_ = tokens_[i];\n uint256 amount_ = amounts_[i];\n uint256 existingAllowance_ = IERC20(token_).allowance(address(this), protocol_);\n\n // making approval 0 first and then re-approving with a new amount.\n SafeERC20.safeApprove(IERC20(address(token_)), protocol_, 0);\n SafeERC20.safeApprove(IERC20(address(token_)), protocol_, amount_);\n _protocolTokens[protocol_].add(token_);\n emit LogAllow(protocol_, token_, amount_, existingAllowance_);\n }\n }\n\n /// @notice Revokes protocols' ability to spend the reserves tokens\n /// @dev The parameters are parallel arrays\n /// @param protocols_ The protocols that will no longer be spending reserve tokens\n /// @param tokens_ The tokens to revoke\n function revoke(address[] memory protocols_, address[] memory tokens_) external onlyAuth {\n if (protocols_.length != tokens_.length) {\n revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);\n }\n\n for (uint256 i = 0; i < protocols_.length; i++) {\n address protocol_ = protocols_[i];\n address token_ = tokens_[i];\n\n SafeERC20.safeApprove(IERC20(address(token_)), protocol_, 0);\n _protocolTokens[protocol_].remove(token_);\n emit LogRevoke(protocol_, token_);\n }\n }\n}\n\n/// @title Reserve Contract\n/// @notice This contract manages the approval of tokens for use by protocols and\n/// the execution of rebalances on protocols\ncontract FluidReserveContract is Error, ReserveContractAuth, UUPSUpgradeable {\n using EnumerableSet for EnumerableSet.AddressSet;\n using SafeERC20 for IERC20;\n\n /// @notice Checks that the sender is a rebalancer\n modifier onlyRebalancer() {\n if (!isRebalancer[msg.sender]) revert FluidReserveContractError(ErrorTypes.ReserveContract__Unauthorized);\n _;\n }\n\n constructor(IFluidLiquidity liquidity_) validAddress(address(liquidity_)) Variables(liquidity_) {\n // ensure logic contract initializer is not abused by disabling initializing\n // see https://forum.openzeppelin.com/t/security-advisory-initialize-uups-implementation-contracts/15301\n // and https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract\n _disableInitializers();\n }\n\n /// @notice initializes the contract\n /// @param _auths The addresses that have the auth to approve and revoke protocol token allowances\n /// @param _rebalancers The addresses that can execute a rebalance on a protocol\n /// @param owner_ owner address is able to upgrade contract and update auth users\n function initialize(\n address[] memory _auths,\n address[] memory _rebalancers,\n address owner_\n ) public initializer validAddress(owner_) {\n for (uint256 i = 0; i < _auths.length; i++) {\n isAuth[_auths[i]] = true;\n emit LogUpdateAuth(_auths[i], true);\n }\n for (uint256 i = 0; i < _rebalancers.length; i++) {\n isRebalancer[_rebalancers[i]] = true;\n emit LogUpdateRebalancer(_rebalancers[i], true);\n }\n _transferOwnership(owner_);\n }\n\n function _authorizeUpgrade(address) internal override onlyOwner {}\n\n /// @notice override renounce ownership as it could leave the contract in an unwanted state if called by mistake.\n function renounceOwnership() public view override onlyOwner {\n revert FluidReserveContractError(ErrorTypes.ReserveContract__RenounceOwnershipUnsupported);\n }\n\n /// @notice Executes a rebalance on a protocol by calling that protocol's `rebalance` function\n /// @param protocol_ The protocol to rebalance\n /// @param value_ any msg.value to send along (as fetched from resolver!)\n function rebalanceFToken(address protocol_, uint256 value_) external payable onlyRebalancer {\n uint256 amount_ = IFTokenAdmin(protocol_).rebalance{ value: value_ }();\n emit LogRebalanceFToken(protocol_, amount_);\n }\n\n /// @notice Executes a rebalance on a protocol by calling that protocol's `rebalance` function\n /// @param protocol_ The protocol to rebalance\n /// @param value_ any msg.value to send along (as fetched from resolver!)\n function rebalanceVault(address protocol_, uint256 value_) external payable onlyRebalancer {\n (int256 colAmount_, int256 debtAmount_) = IFluidVaultT1(protocol_).rebalance{ value: value_ }();\n\n IFluidVaultT1.ConstantViews memory constants_ = IFluidVaultT1(protocol_).constantsView();\n if (constants_.supplyToken == NATIVE_TOKEN_ADDRESS) {\n if (value_ > 0 && colAmount_ < 0) {\n revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);\n }\n }\n\n if (constants_.borrowToken == NATIVE_TOKEN_ADDRESS) {\n if (value_ > 0 && debtAmount_ > 0) {\n revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);\n }\n }\n\n if (value_ > 0 && !(constants_.supplyToken == NATIVE_TOKEN_ADDRESS || constants_.borrowToken == NATIVE_TOKEN_ADDRESS)) {\n revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);\n }\n\n emit LogRebalanceVault(protocol_, colAmount_, debtAmount_);\n }\n\n function transferFunds(address[] calldata tokens_) external virtual onlyAuth {\n for (uint256 i = 0; i < tokens_.length; i++) {\n SafeTransfer.safeTransfer(\n address(tokens_[i]),\n address(LIQUIDITY),\n IERC20(tokens_[i]).balanceOf(address(this))\n );\n emit LogTransferFunds(tokens_[i]);\n }\n }\n\n /// @notice Gets the tokens that are approved for use by a protocol\n /// @param protocol_ The protocol to get the tokens for\n /// @return result_ The tokens that are approved for use by the protocol\n function getProtocolTokens(address protocol_) external view returns (address[] memory result_) {\n EnumerableSet.AddressSet storage tokens_ = _protocolTokens[protocol_];\n result_ = new address[](tokens_.length());\n for (uint256 i = 0; i < tokens_.length(); i++) {\n result_[i] = tokens_.at(i);\n }\n }\n\n /// @notice allow receive native token\n receive() external payable {}\n}\n" }, "contracts/reserve/proxy.sol": { "content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { ERC1967Proxy } from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\n\n/// @title FluidReserveContractProxy\n/// @notice Default ERC1967Proxy for ReserveContract\ncontract FluidReserveContractProxy is ERC1967Proxy {\n constructor(address logic_, bytes memory data_) payable ERC1967Proxy(logic_, data_) {}\n}\n" }, "contracts/reserve/variables.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Initializable } from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport { OwnableUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport { EnumerableSet } from \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\nimport { IFluidLiquidity } from \"../liquidity/interfaces/iLiquidity.sol\";\nimport { IFluidLendingFactory } from \"../protocols/lending/interfaces/iLendingFactory.sol\";\n\nabstract contract Constants {\n /// @notice address of the liquidity contract\n IFluidLiquidity public immutable LIQUIDITY;\n\n /// @dev address that is mapped to the chain native token\n address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n constructor(IFluidLiquidity liquidity_) {\n LIQUIDITY = liquidity_;\n }\n}\n\nabstract contract Variables is Constants, Initializable, OwnableUpgradeable {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n // ------------ storage variables from inherited contracts (Initializable, OwnableUpgradeable) come before vars here --------\n // @dev variables here start at storage slot 101, before is:\n // - Initializable with storage slot 0:\n // uint8 private _initialized;\n // bool private _initializing;\n // - OwnableUpgradeable with slots 1 to 100:\n // uint256[50] private __gap; (from ContextUpgradeable, slot 1 until slot 50)\n // address private _owner; (at slot 51)\n // uint256[49] private __gap; (slot 52 until slot 100)\n\n // ----------------------- slot 101 ---------------------------\n /// @notice Maps address to there status as an Auth\n mapping(address => bool) public isAuth;\n\n /// @notice Maps address to there status as a Rebalancer\n mapping(address => bool) public isRebalancer;\n\n /// @notice Mapping of protocol addresses to the tokens that are allowed to be used by that protocol\n mapping(address => EnumerableSet.AddressSet) internal _protocolTokens;\n\n constructor(IFluidLiquidity liquidity_) Constants(liquidity_) {}\n}\n" }, "solmate/src/auth/Owned.sol": { "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)\nabstract contract Owned {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event OwnershipTransferred(address indexed user, address indexed newOwner);\n\n /*//////////////////////////////////////////////////////////////\n OWNERSHIP STORAGE\n //////////////////////////////////////////////////////////////*/\n\n address public owner;\n\n modifier onlyOwner() virtual {\n require(msg.sender == owner, \"UNAUTHORIZED\");\n\n _;\n }\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(address _owner) {\n owner = _owner;\n\n emit OwnershipTransferred(address(0), _owner);\n }\n\n /*//////////////////////////////////////////////////////////////\n OWNERSHIP LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function transferOwnership(address newOwner) public virtual onlyOwner {\n owner = newOwner;\n\n emit OwnershipTransferred(msg.sender, newOwner);\n }\n}\n" }, "solmate/src/utils/Bytes32AddressLib.sol": { "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Library for converting between addresses and bytes32 values.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol)\nlibrary Bytes32AddressLib {\n function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) {\n return address(uint160(uint256(bytesValue)));\n }\n\n function fillLast12Bytes(address addressValue) internal pure returns (bytes32) {\n return bytes32(bytes20(addressValue));\n }\n}\n" }, "solmate/src/utils/CREATE3.sol": { "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {Bytes32AddressLib} from \"./Bytes32AddressLib.sol\";\n\n/// @notice Deploy to deterministic addresses without an initcode factor.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/CREATE3.sol)\n/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol)\nlibrary CREATE3 {\n using Bytes32AddressLib for bytes32;\n\n //--------------------------------------------------------------------------------//\n // Opcode | Opcode + Arguments | Description | Stack View //\n //--------------------------------------------------------------------------------//\n // 0x36 | 0x36 | CALLDATASIZE | size //\n // 0x3d | 0x3d | RETURNDATASIZE | 0 size //\n // 0x3d | 0x3d | RETURNDATASIZE | 0 0 size //\n // 0x37 | 0x37 | CALLDATACOPY | //\n // 0x36 | 0x36 | CALLDATASIZE | size //\n // 0x3d | 0x3d | RETURNDATASIZE | 0 size //\n // 0x34 | 0x34 | CALLVALUE | value 0 size //\n // 0xf0 | 0xf0 | CREATE | newContract //\n //--------------------------------------------------------------------------------//\n // Opcode | Opcode + Arguments | Description | Stack View //\n //--------------------------------------------------------------------------------//\n // 0x67 | 0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode | bytecode //\n // 0x3d | 0x3d | RETURNDATASIZE | 0 bytecode //\n // 0x52 | 0x52 | MSTORE | //\n // 0x60 | 0x6008 | PUSH1 08 | 8 //\n // 0x60 | 0x6018 | PUSH1 18 | 24 8 //\n // 0xf3 | 0xf3 | RETURN | //\n //--------------------------------------------------------------------------------//\n bytes internal constant PROXY_BYTECODE = hex\"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3\";\n\n bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE);\n\n function deploy(\n bytes32 salt,\n bytes memory creationCode,\n uint256 value\n ) internal returns (address deployed) {\n bytes memory proxyChildBytecode = PROXY_BYTECODE;\n\n address proxy;\n /// @solidity memory-safe-assembly\n assembly {\n // Deploy a new contract with our pre-made bytecode via CREATE2.\n // We start 32 bytes into the code to avoid copying the byte length.\n proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt)\n }\n require(proxy != address(0), \"DEPLOYMENT_FAILED\");\n\n deployed = getDeployed(salt);\n (bool success, ) = proxy.call{value: value}(creationCode);\n require(success && deployed.code.length != 0, \"INITIALIZATION_FAILED\");\n }\n\n function getDeployed(bytes32 salt) internal view returns (address) {\n address proxy = keccak256(\n abi.encodePacked(\n // Prefix:\n bytes1(0xFF),\n // Creator:\n address(this),\n // Salt:\n salt,\n // Bytecode hash:\n PROXY_BYTECODE_HASH\n )\n ).fromLast20Bytes();\n\n return\n keccak256(\n abi.encodePacked(\n // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01)\n // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)\n hex\"d6_94\",\n proxy,\n hex\"01\" // Nonce of the proxy contract (1)\n )\n ).fromLast20Bytes();\n }\n}\n" }, "solmate/src/utils/FixedPointMathLib.sol": { "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Arithmetic library with operations for fixed-point numbers.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)\n/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)\nlibrary FixedPointMathLib {\n /*//////////////////////////////////////////////////////////////\n SIMPLIFIED FIXED POINT OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n uint256 internal constant MAX_UINT256 = 2**256 - 1;\n\n uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.\n\n function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.\n }\n\n function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.\n }\n\n function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.\n }\n\n function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.\n }\n\n /*//////////////////////////////////////////////////////////////\n LOW LEVEL FIXED POINT OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function mulDivDown(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))\n if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {\n revert(0, 0)\n }\n\n // Divide x * y by the denominator.\n z := div(mul(x, y), denominator)\n }\n }\n\n function mulDivUp(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))\n if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {\n revert(0, 0)\n }\n\n // If x * y modulo the denominator is strictly greater than 0,\n // 1 is added to round up the division of x * y by the denominator.\n z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))\n }\n }\n\n function rpow(\n uint256 x,\n uint256 n,\n uint256 scalar\n ) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n switch x\n case 0 {\n switch n\n case 0 {\n // 0 ** 0 = 1\n z := scalar\n }\n default {\n // 0 ** n = 0\n z := 0\n }\n }\n default {\n switch mod(n, 2)\n case 0 {\n // If n is even, store scalar in z for now.\n z := scalar\n }\n default {\n // If n is odd, store x in z for now.\n z := x\n }\n\n // Shifting right by 1 is like dividing by 2.\n let half := shr(1, scalar)\n\n for {\n // Shift n right by 1 before looping to halve it.\n n := shr(1, n)\n } n {\n // Shift n right by 1 each iteration to halve it.\n n := shr(1, n)\n } {\n // Revert immediately if x ** 2 would overflow.\n // Equivalent to iszero(eq(div(xx, x), x)) here.\n if shr(128, x) {\n revert(0, 0)\n }\n\n // Store x squared.\n let xx := mul(x, x)\n\n // Round to the nearest number.\n let xxRound := add(xx, half)\n\n // Revert if xx + half overflowed.\n if lt(xxRound, xx) {\n revert(0, 0)\n }\n\n // Set x to scaled xxRound.\n x := div(xxRound, scalar)\n\n // If n is even:\n if mod(n, 2) {\n // Compute z * x.\n let zx := mul(z, x)\n\n // If z * x overflowed:\n if iszero(eq(div(zx, x), z)) {\n // Revert if x is non-zero.\n if iszero(iszero(x)) {\n revert(0, 0)\n }\n }\n\n // Round to the nearest number.\n let zxRound := add(zx, half)\n\n // Revert if zx + half overflowed.\n if lt(zxRound, zx) {\n revert(0, 0)\n }\n\n // Return properly scaled zxRound.\n z := div(zxRound, scalar)\n }\n }\n }\n }\n }\n\n /*//////////////////////////////////////////////////////////////\n GENERAL NUMBER UTILITIES\n //////////////////////////////////////////////////////////////*/\n\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n let y := x // We start y at x, which will help us make our initial estimate.\n\n z := 181 // The \"correct\" value is 1, but this saves a multiplication later.\n\n // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad\n // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.\n\n // We check y >= 2^(k + 8) but shift right by k bits\n // each branch to ensure that if x >= 256, then y >= 256.\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n // Goal was to get z*z*y within a small factor of x. More iterations could\n // get y in a tighter range. Currently, we will have y in [256, 256*2^16).\n // We ensured y >= 256 so that the relative difference between y and y+1 is small.\n // That's not possible if x < 256 but we can just verify those cases exhaustively.\n\n // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.\n // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.\n // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.\n\n // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range\n // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.\n\n // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate\n // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.\n\n // There is no overflow risk here since y < 2^136 after the first branch above.\n z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.\n\n // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n // If x+1 is a perfect square, the Babylonian method cycles between\n // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.\n // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division\n // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.\n // If you don't care whether the floor or ceil square root is returned, you can remove this statement.\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n // Mod x by y. Note this will return\n // 0 instead of reverting if y is zero.\n z := mod(x, y)\n }\n }\n\n function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {\n /// @solidity memory-safe-assembly\n assembly {\n // Divide x by y. Note this will return\n // 0 instead of reverting if y is zero.\n r := div(x, y)\n }\n }\n\n function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n // Add 1 to x * y if x % y > 0. Note this will\n // return 0 instead of reverting if y is zero.\n z := add(gt(mod(x, y), 0), div(x, y))\n }\n }\n}\n" }, "solmate/src/utils/SSTORE2.sol": { "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Read and write to persistent storage at a fraction of the cost.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)\n/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)\nlibrary SSTORE2 {\n uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.\n\n /*//////////////////////////////////////////////////////////////\n WRITE LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function write(bytes memory data) internal returns (address pointer) {\n // Prefix the bytecode with a STOP opcode to ensure it cannot be called.\n bytes memory runtimeCode = abi.encodePacked(hex\"00\", data);\n\n bytes memory creationCode = abi.encodePacked(\n //---------------------------------------------------------------------------------------------------------------//\n // Opcode | Opcode + Arguments | Description | Stack View //\n //---------------------------------------------------------------------------------------------------------------//\n // 0x60 | 0x600B | PUSH1 11 | codeOffset //\n // 0x59 | 0x59 | MSIZE | 0 codeOffset //\n // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset //\n // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset //\n // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset //\n // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset //\n // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //\n // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //\n // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) //\n // 0xf3 | 0xf3 | RETURN | //\n //---------------------------------------------------------------------------------------------------------------//\n hex\"60_0B_59_81_38_03_80_92_59_39_F3\", // Returns all code in the contract except for the first 11 (0B in hex) bytes.\n runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.\n );\n\n /// @solidity memory-safe-assembly\n assembly {\n // Deploy a new contract with the generated creation code.\n // We start 32 bytes into the code to avoid copying the byte length.\n pointer := create(0, add(creationCode, 32), mload(creationCode))\n }\n\n require(pointer != address(0), \"DEPLOYMENT_FAILED\");\n }\n\n /*//////////////////////////////////////////////////////////////\n READ LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function read(address pointer) internal view returns (bytes memory) {\n return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);\n }\n\n function read(address pointer, uint256 start) internal view returns (bytes memory) {\n start += DATA_OFFSET;\n\n return readBytecode(pointer, start, pointer.code.length - start);\n }\n\n function read(\n address pointer,\n uint256 start,\n uint256 end\n ) internal view returns (bytes memory) {\n start += DATA_OFFSET;\n end += DATA_OFFSET;\n\n require(pointer.code.length >= end, \"OUT_OF_BOUNDS\");\n\n return readBytecode(pointer, start, end - start);\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL HELPER LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function readBytecode(\n address pointer,\n uint256 start,\n uint256 size\n ) private view returns (bytes memory data) {\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n data := mload(0x40)\n\n // Update the free memory pointer to prevent overriding our data.\n // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).\n // Adding 31 to size and running the result through the logic above ensures\n // the memory pointer remains word-aligned, following the Solidity convention.\n mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))\n\n // Store the size of the data in the first 32 byte chunk of free memory.\n mstore(data, size)\n\n // Copy the code into memory right after the 32 bytes we used to store the size.\n extcodecopy(pointer, add(data, 32), start, size)\n }\n }\n}\n" } }, "settings": { "optimizer": { "enabled": true, "runs": 10000000 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "metadata", "devdoc", "userdoc", "storageLayout", "evm.gasEstimates" ], "": [ "ast" ] } }, "metadata": { "useLiteralContent": true } } }