{ "language": "Solidity", "sources": { "@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/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/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" }, "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" }, "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/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 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(uint256 totalSupply, uint256 oldMaxDebtCeiling, uint256 maxDebtCeiling);\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\n function getTotalSupply() public view returns (uint256 totalSupply_) {\n uint256 exchangePriceAndConfig_ = LIQUIDITY_RESOLVER.getExchangePricesAndConfig(BORROW_TOKEN);\n uint256 totalAmounts_ = LIQUIDITY_RESOLVER.getTotalAmounts(BORROW_TOKEN);\n\n (uint256 supplyExchangePrice_, ) = LiquidityCalcs.calcExchangePrices(exchangePriceAndConfig_);\n\n totalSupply_ = LiquidityCalcs.getTotalSupply(totalAmounts_, supplyExchangePrice_);\n }\n\n /// @notice returns the currently set max debt ceiling.\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.\n function calcMaxDebtCeiling() public view returns (uint256 maxDebtCeiling_) {\n (maxDebtCeiling_, ) = _calcMaxDebtCeiling(\n getTotalSupply(),\n LIQUIDITY.readFromStorage(_LIQUDITY_PROTOCOL_BORROW_SLOT)\n );\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 (configPercentDiff_, , , , , ) = _configPercentDiff();\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 _calcMaxDebtCeiling(\n uint256 totalSupply_,\n uint256 userBorrowData_\n ) public view returns (uint256 maxDebtCeiling_, uint256 baseDebtCeiling_) {\n maxDebtCeiling_ = (MAX_UTILIZATION * totalSupply_) / MAX_UTILIZATION_PRECISION;\n\n baseDebtCeiling_ = BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (baseDebtCeiling_ > maxDebtCeiling_) {\n // max debt ceiling can never be < base debt ceiling\n maxDebtCeiling_ = baseDebtCeiling_;\n }\n }\n\n function _configPercentDiff()\n internal\n view\n returns (\n uint256 configPercentDiff_,\n uint256 userBorrowData_,\n uint256 totalSupply_,\n uint256 oldMaxDebtCeiling_,\n uint256 maxDebtCeiling_,\n uint256 baseDebtCeiling_\n )\n {\n userBorrowData_ = LIQUIDITY.readFromStorage(_LIQUDITY_PROTOCOL_BORROW_SLOT); // total storage slot\n\n oldMaxDebtCeiling_ = BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n totalSupply_ = getTotalSupply();\n (maxDebtCeiling_, baseDebtCeiling_) = _calcMaxDebtCeiling(totalSupply_, userBorrowData_);\n\n if (oldMaxDebtCeiling_ == maxDebtCeiling_) {\n return (0, userBorrowData_, totalSupply_, oldMaxDebtCeiling_, maxDebtCeiling_, baseDebtCeiling_);\n }\n\n if (oldMaxDebtCeiling_ > maxDebtCeiling_) {\n // % of how much new max debt ceiling would be smaller\n configPercentDiff_ = oldMaxDebtCeiling_ - maxDebtCeiling_;\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_ = maxDebtCeiling_ - oldMaxDebtCeiling_;\n // e.g. 10 - 8 = 2. 2 * 10000 / 8 -> 2500 (25%)\n }\n\n configPercentDiff_ = (configPercentDiff_ * 1e4) / oldMaxDebtCeiling_;\n }\n\n function _updateBorrowLimits() internal returns (bool updated_) {\n (\n uint256 configPercentDiff_,\n uint256 userBorrowData_,\n uint256 totalSupply_,\n uint256 oldMaxDebtCeiling_,\n uint256 maxDebtCeiling_,\n uint256 baseDebtCeiling_\n ) = _configPercentDiff();\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: baseDebtCeiling_, // set same as old\n maxDebtCeiling: maxDebtCeiling_\n });\n LIQUIDITY.updateUserBorrowConfigs(userBorrowConfigs_);\n\n emit LogUpdateBorrowMaxDebtCeiling(totalSupply_, oldMaxDebtCeiling_, maxDebtCeiling_);\n\n return true;\n }\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/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/bigMathVault.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { BigMathMinified } from \"./bigMathMinified.sol\";\n\n/// @title Extended version of BigMathMinified. Implements functions for normal operators (*, /, etc) modified to interact with big numbers.\n/// @notice this is an optimized version mainly created by taking Fluid vault's codebase into consideration so it's use is limited for other cases.\n// \n// @dev IMPORTANT: for any change here, make sure to uncomment and run the fuzz tests in bigMathVault.t.sol\nlibrary BigMathVault {\n uint private constant COEFFICIENT_SIZE_DEBT_FACTOR = 35;\n uint private constant EXPONENT_SIZE_DEBT_FACTOR = 15;\n uint private constant COEFFICIENT_MAX_DEBT_FACTOR = (1 << COEFFICIENT_SIZE_DEBT_FACTOR) - 1;\n uint private constant EXPONENT_MAX_DEBT_FACTOR = (1 << EXPONENT_SIZE_DEBT_FACTOR) - 1;\n uint private constant DECIMALS_DEBT_FACTOR = 16384;\n uint internal constant MAX_MASK_DEBT_FACTOR = (1 << (COEFFICIENT_SIZE_DEBT_FACTOR + EXPONENT_SIZE_DEBT_FACTOR)) - 1;\n\n // Having precision as 2**64 on vault\n uint internal constant PRECISION = 64;\n uint internal constant TWO_POWER_64 = 1 << PRECISION;\n // Max bit for 35 bits * 35 bits number will be 70\n // why do we use 69 then here instead of 70\n uint internal constant TWO_POWER_69_MINUS_1 = (1 << 69) - 1;\n\n uint private constant COEFFICIENT_PLUS_PRECISION = COEFFICIENT_SIZE_DEBT_FACTOR + PRECISION; // 99\n uint private constant COEFFICIENT_PLUS_PRECISION_MINUS_1 = COEFFICIENT_PLUS_PRECISION - 1; // 98\n uint private constant TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1 = (1 << COEFFICIENT_PLUS_PRECISION_MINUS_1) - 1; // (1 << 98) - 1;\n uint private constant TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1_MINUS_1 =\n (1 << (COEFFICIENT_PLUS_PRECISION_MINUS_1 - 1)) - 1; // (1 << 97) - 1;\n\n /// @dev multiplies a `normal` number with a `bigNumber1` and then divides by `bigNumber2`.\n /// @dev For vault's use case MUST always:\n /// - bigNumbers have exponent size 15 bits\n /// - bigNumbers 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 /// - 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 /***********************************|\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 udpate 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 be 0 but never negative)\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 uint256 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) and rate at y2 can not be < rate at y1\n // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS\n slope_ = ((y2_ - y1_) * TWELVE_DECIMALS) / (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) - int256(slope_ * 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 rate_ = (uint256(int256(slope_ * utilization_) + constant_)) / 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 be 0 but never negative)\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 uint256 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) and rate at y2 can not be < rate at y1\n // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS\n slope_ = ((y2_ - y1_) * TWELVE_DECIMALS) / (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) - int256(slope_ * 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 rate_ = (uint256(int256(slope_ * utilization_) + constant_)) / 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\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\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 // --------------------------------\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 }\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\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/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/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" }, "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 {\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 int24 arithmeticMeanTick_ = 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 arithmeticMeanTick_--;\n }\n\n // get the current uniswap price, which is the last tick cumulatives interval, usually [..., 1, 0]\n exchangeRate_ = _getPriceFromSqrtPriceX96(TickMath.getSqrtRatioAtTick(arithmeticMeanTick_));\n }\n if (_UNIV3_INVERT_RATE) {\n exchangeRate_ = _invertUniV3Price(exchangeRate_);\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 exchangeRate_,\n tickCumulatives_[1] - tickCumulatives_[0],\n _UNI_TWAP1_INTERVAL,\n _UNI_TWAP1_MAX_DELTA_PERCENT\n )\n ) {\n return 0;\n }\n\n // TWAP 2 check\n if (\n _isInvalidTWAPDelta(\n exchangeRate_,\n tickCumulatives_[2] - tickCumulatives_[1],\n _UNI_TWAP2_INTERVAL,\n _UNI_TWAP2_MAX_DELTA_PERCENT\n )\n ) {\n return 0;\n }\n\n // TWAP 3 check\n if (\n _isInvalidTWAPDelta(\n exchangeRate_,\n tickCumulatives_[3] - tickCumulatives_[2],\n _UNI_TWAP3_INTERVAL,\n _UNI_TWAP3_MAX_DELTA_PERCENT\n )\n ) {\n return 0;\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 uint256 exchangeRate_,\n int256 tickCumulativesDelta_,\n int256 interval_, // can not be 0 because of constructor sanity checks\n uint256 maxDelta_\n ) internal view returns (bool) {\n unchecked {\n int24 arithmeticMeanTick_ = int24(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 // Get the price for the interval of the twap\n uint256 price_ = _getPriceFromSqrtPriceX96(TickMath.getSqrtRatioAtTick(arithmeticMeanTick_));\n if (_UNIV3_INVERT_RATE) {\n price_ = _invertUniV3Price(price_);\n }\n // Check that the uniswapPrice is within DELTA of the Uniswap TWAP\n maxDelta_ = (price_ * maxDelta_) / OracleUtils.HUNDRED_PERCENT_DELTA_SCALER;\n if (exchangeRate_ > (price_ + maxDelta_) || exchangeRate_ < (price_ - maxDelta_)) {\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 /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27\n function getExchangeRate() 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 getExchangeRate() external 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" }, "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 getExchangeRate() external view override returns (uint256 exchangeRate_) {\n (exchangeRate_, ) = _getRateWithFallback();\n\n if (exchangeRate_ == 0) {\n revert FluidOracleError(ErrorTypes.FallbackCLRSOracle__ExchangeRateZero);\n }\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 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 getExchangeRate() public view virtual override 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 /// @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" }, "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 getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return _getWeETHExchangeRate();\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 getExchangeRate() 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" }, "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 getExchangeRate() public view override(FluidOracle, UniV3CheckCLRSOracle) returns (uint256 exchangeRate_) {\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.getExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\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 getExchangeRate() external 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 /// @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 getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return _getWstETHExchangeRate();\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 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 `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/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 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;\n uint256 borrowable; // actual currently borrowable amount (borrow limit - already borrowed) & considering balance\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\";\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 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 LiquidityStructs } 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 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 configs_.oraclePrice = IFluidOracle(configs_.oracle).getExchangeRate();\n configs_.rebalancer = getRebalancer(vault_);\n }\n\n function getExchangePricesAndRates(\n address vault_,\n Configs memory configs_,\n LiquidityStructs.OverallTokenData memory liquiditySupplytokenData_,\n LiquidityStructs.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 }\n\n function getLimitsAndAvailability(\n TotalSupplyAndBorrow memory totalSupplyAndBorrow_,\n ExchangePricesAndRates memory exchangePricesAndRates_,\n IFluidVaultT1.ConstantViews memory constantsVariables_,\n Configs memory configs_\n ) internal view returns (LimitsAndAvailability memory limitsAndAvailability_) {\n // fetching user's supply slot data\n uint userSupplyLiquidityData_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserSupplySlot);\n uint userBorrowLiquidityData_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserBorrowSlot);\n\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(userSupplyLiquidityData_, userSupply_);\n\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 uint borrowLimitRaw_ = LiquidityCalcs.calcBorrowLimitBeforeOperate(userBorrowLiquidityData_, userBorrow_);\n\n limitsAndAvailability_.withdrawLimit =\n (supplyLimitRaw_ * exchangePricesAndRates_.liquiditySupplyExchangePrice) /\n 1e12;\n limitsAndAvailability_.borrowLimit =\n (borrowLimitRaw_ * exchangePricesAndRates_.liquidityBorrowExchangePrice) /\n 1e12;\n limitsAndAvailability_.borrowableUntilLimit = ((limitsAndAvailability_.borrowLimit * 999999) / 1000000);\n limitsAndAvailability_.borrowableUntilLimit = (limitsAndAvailability_.borrowableUntilLimit >\n totalSupplyAndBorrow_.totalBorrowLiquidity)\n ? limitsAndAvailability_.borrowableUntilLimit - totalSupplyAndBorrow_.totalBorrowLiquidity\n : 0;\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 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 > withdrawalGap_)\n ? (((limitsAndAvailability_.withdrawableUntilLimit - withdrawalGap_) * 999999) / 1000000)\n : 0;\n\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 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 LiquidityStructs.UserSupplyData memory liquidityUserSupplyData_,\n LiquidityStructs.OverallTokenData memory liquiditySupplytokenData_\n ) = LIQUIDITY_RESOLVER.getUserSupplyData(vault_, vaultData_.constantVariables.supplyToken);\n\n (\n LiquidityStructs.UserBorrowData memory liquidityUserBorrowData_,\n LiquidityStructs.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 );\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\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 = (TickMath.getRatioAtTick(int24(userPosition_.tick)) * userPosition_.supply) >> 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 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 address[] memory vaults_ = new address[](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 reason) {\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 reason) {\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 bytes memory errorData_;\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 // TODO: Need Branch & ticks related data?\n // TODO: Branch history?\n\n constructor(\n address factory_,\n address liquidity_,\n address liquidityResolver_\n ) Helpers(factory_, liquidity_, liquidityResolver_) {}\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 reason) {\n } catch (bytes memory lowLevelData_) {\n }\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 oraclePrice;\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;\n uint borrowable;\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/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\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 /***********************************|\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 /// @notice SSTORE2 pointer for the VaultT1 creation code. Stored externally to reduce factory bytecode\n address public immutable VAULT_T1_CREATIONCODE_ADDRESS;\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 VAULT_T1_CREATIONCODE_ADDRESS = SSTORE2.write(type(FluidVaultT1).creationCode);\n ADDRESS_THIS = address(this);\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 /// @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(SSTORE2.read(VAULT_T1_CREATIONCODE_ADDRESS), abi.encode(constants_));\n\n emit VaultT1Deployed(vault_, constants_.vaultId, supplyToken_, borrowToken_);\n\n return vaultCreationBytecode_;\n }\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/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 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 vaultVariables_ = vaultVariables;\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 profit, meaning withdrawn from vault and sent to rebalancer address.\n /// if `colAmt_` is negative then loss, meaning transfer from rebalancer address to vault and deposit.\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\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.\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 // if debt is greater than 0 & transaction includes borrow or withdraw (incl. combinations such as deposit + borrow etc.)\n // -> check collateral factor\n if (o_.debtRaw > 0) {\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))).getExchangeRate();\n // Note if price would come back as 0 `getTickAtRatio` will fail\n\n // Converting price in terms of raw amounts\n temp_ = (temp_ * o_.supplyExPrice) / o_.borrowExPrice;\n\n unchecked {\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_ < 1e45)\n ? ((temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27)\n : (temp2_ / 1e27) * TickMath.ZERO_TICK_SCALED_RATIO;\n }\n\n // temp3_ => tickAtCF_\n (temp3_, ) = TickMath.getTickAtRatio(temp2_);\n if (o_.tick > temp3_) {\n unchecked {\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 if (\n o_.oldTick <= o_.tick ||\n (o_.debtRaw - o_.dustDebtRaw) > (((o_.oldNetDebtRaw * 1000000001) / 1000000000) + 1)\n ) {\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 (debtAmt_ < 10000 || debtAmt_ > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidationAmt);\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 // ############# 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 // 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 // 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 // ############# 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))).getExchangeRate(); // Price in 27 decimals\n // temp_ -> debtPerCol Converting in terms of raw amount\n temp_ = (temp_ * memoryVars_.supplyExPrice) / memoryVars_.borrowExPrice;\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 unchecked {\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_ < 1e45)\n ? ((temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27)\n : ((temp_ / 1e27) * TickMath.ZERO_TICK_SCALED_RATIO);\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 // 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 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 if (\n currentData_.tick > memoryVars_.liquidationTick && // current tick > liquidation tick\n currentData_.tick <= memoryVars_.maxTick // current tick <= max tick\n ) {\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 < memoryVars_.liquidationTick) {\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 memoryVars_.liquidationTick - 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 memoryVars_.liquidationTick\n unchecked {\n tickInfo_.tick += 2;\n }\n tickInfo_.partials = 1;\n } else {\n // Increasing tick by 1 as final ratio will probably be a partial\n unchecked {\n ++tickInfo_.tick;\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 }\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 (((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 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() public {\n _spell(SECONDARY_IMPLEMENTATION, msg.data);\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() public _verifyCaller {\n uint256 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 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 int maxTick_;\n\n {\n a_.vaultVariables2 = vaultVariables2;\n\n // temp_ -> top tick\n temp_ = ((vaultVariables_ >> 2) & X20);\n if (temp_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__TopTickDoesNotExist);\n }\n\n // Below are exchange prices of vaults\n (, , a_.supplyExPrice, a_.borrowExPrice) = IFluidVaultT1(address(this)).updateExchangePrices(a_.vaultVariables2);\n\n {\n // Col price w.r.t debt. For example: 1 ETH = 1000 DAI\n // temp2_ -> debtPerCol\n temp2_ = IFluidOracle(address(uint160(a_.vaultVariables2 >> 96))).getExchangeRate(); // Price in 27 decimals\n // Converting in terms of raw amount\n temp2_ = (temp2_ * a_.supplyExPrice) / a_.borrowExPrice;\n\n temp2_ = (temp2_ < 1e45)\n ? (temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27\n : ((temp2_ / 1e27) * TickMath.ZERO_TICK_SCALED_RATIO);\n\n // Max threshold in 3 decimals (900 = 90%) -> (vaultVariables2 >> 52) & X10\n // temp2_ -> maxRatio_\n temp2_ = (temp2_ * ((a_.vaultVariables2 >> 52) & X10)) / 1000;\n (maxTick_, ) = TickMath.getTickAtRatio(temp2_);\n }\n }\n\n TickHasDebt memory tickHasDebt_;\n\n {\n // liquidating ticks above max ratio\n\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 // Updating vault variables on storage\n // Absorb does not do any changes in total supply & total borrow. Hence no need to update total borrow & total supply.\n // this also resets the reentrancy bit\n vaultVariables = vaultVariables_;\n\n emit LogAbsorb(a_.colAbsorbed, a_.debtAbsorbed);\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; // total debt - dust debt\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 supplyExPrice;\n uint256 borrowExPrice;\n uint256 debtAbsorbed;\n uint256 colAbsorbed;\n uint256 vaultVariables2;\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/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" }, "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 } } }