mirror of
https://github.com/Instadapp/fluid-contracts-public.git
synced 2024-07-29 21:57:37 +00:00
d7a58e88ff
ARB: deploy protocols
366 lines
790 KiB
JSON
366 lines
790 KiB
JSON
{
|
||
"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/security/ReentrancyGuard.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor() {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be _NOT_ENTERED\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/token/ERC20/ERC20.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _transfer(owner, to, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n * `transferFrom`. This is semantically equivalent to an infinite approval.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * NOTE: Does not update the allowance if the current allowance\n * is the maximum `uint256`.\n *\n * Requirements:\n *\n * - `from` and `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n * - the caller must have allowance for ``from``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual override returns (bool) {\n address spender = _msgSender();\n _spendAllowance(from, spender, amount);\n _transfer(from, to, amount);\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, allowance(owner, spender) + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n address owner = _msgSender();\n uint256 currentAllowance = allowance(owner, spender);\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(owner, spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `from` to `to`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n */\n function _transfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {\n require(from != address(0), \"ERC20: transfer from the zero address\");\n require(to != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, amount);\n\n uint256 fromBalance = _balances[from];\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[from] = fromBalance - amount;\n // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n // decrementing then incrementing.\n _balances[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n _afterTokenTransfer(from, to, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n unchecked {\n // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n _balances[account] += amount;\n }\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n // Overflow not possible: amount <= accountBalance <= totalSupply.\n _totalSupply -= amount;\n }\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n *\n * Does not update the allowance amount in case of infinite allowance.\n * Revert if not enough allowance is available.\n *\n * Might emit an {Approval} event.\n */\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./draft-IERC20Permit.sol\";\nimport \"../ERC20.sol\";\nimport \"../../../utils/cryptography/ECDSA.sol\";\nimport \"../../../utils/cryptography/EIP712.sol\";\nimport \"../../../utils/Counters.sol\";\n\n/**\n * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * _Available since v3.4._\n */\nabstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {\n using Counters for Counters.Counter;\n\n mapping(address => Counters.Counter) private _nonces;\n\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private constant _PERMIT_TYPEHASH =\n keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n /**\n * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.\n * However, to ensure consistency with the upgradeable transpiler, we will continue\n * to reserve a slot.\n * @custom:oz-renamed-from _PERMIT_TYPEHASH\n */\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;\n\n /**\n * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `\"1\"`.\n *\n * It's a good idea to use the same `name` that is defined as the ERC20 token name.\n */\n constructor(string memory name) EIP712(name, \"1\") {}\n\n /**\n * @dev See {IERC20Permit-permit}.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual override {\n require(block.timestamp <= deadline, \"ERC20Permit: expired deadline\");\n\n bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));\n\n bytes32 hash = _hashTypedDataV4(structHash);\n\n address signer = ECDSA.recover(hash, v, r, s);\n require(signer == owner, \"ERC20Permit: invalid signature\");\n\n _approve(owner, spender, value);\n }\n\n /**\n * @dev See {IERC20Permit-nonces}.\n */\n function nonces(address owner) public view virtual override returns (uint256) {\n return _nonces[owner].current();\n }\n\n /**\n * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view override returns (bytes32) {\n return _domainSeparatorV4();\n }\n\n /**\n * @dev \"Consume a nonce\": return the current value and increment.\n *\n * _Available since v4.1._\n */\n function _useNonce(address owner) internal virtual returns (uint256 current) {\n Counters.Counter storage nonce = _nonces[owner];\n current = nonce.current();\n nonce.increment();\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721Enumerable is IERC721 {\n /**\n * @dev Returns the total amount of tokens stored by the contract.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns a token ID owned by `owner` at a given `index` of its token list.\n * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.\n */\n function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);\n\n /**\n * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.\n * Use along with {totalSupply} to enumerate all tokens.\n */\n function tokenByIndex(uint256 index) external view returns (uint256);\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/token/ERC721/IERC721.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721\n * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n * understand this adds an external call which potentially creates a reentrancy vulnerability.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/Address.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/Context.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/Counters.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title Counters\n * @author Matt Condon (@shrugs)\n * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number\n * of elements in a mapping, issuing ERC721 ids, or counting request ids.\n *\n * Include with `using Counters for Counters.Counter;`\n */\nlibrary Counters {\n struct Counter {\n // This variable should never be directly accessed by users of the library: interactions must be restricted to\n // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add\n // this feature: see https://github.com/ethereum/solidity/issues/4637\n uint256 _value; // default: 0\n }\n\n function current(Counter storage counter) internal view returns (uint256) {\n return counter._value;\n }\n\n function increment(Counter storage counter) internal {\n unchecked {\n counter._value += 1;\n }\n }\n\n function decrement(Counter storage counter) internal {\n uint256 value = counter._value;\n require(value > 0, \"Counter: decrement overflow\");\n unchecked {\n counter._value = value - 1;\n }\n }\n\n function reset(Counter storage counter) internal {\n counter._value = 0;\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Strings.sol\";\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV // Deprecated in v4.8\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n /// @solidity memory-safe-assembly\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/cryptography/EIP712.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./ECDSA.sol\";\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.\n *\n * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,\n * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding\n * they need in their contracts using a combination of `abi.encode` and `keccak256`.\n *\n * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding\n * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA\n * ({_hashTypedDataV4}).\n *\n * The implementation of the domain separator was designed to be as efficient as possible while still properly updating\n * the chain id to protect against replay attacks on an eventual fork of the chain.\n *\n * NOTE: This contract implements the version of the encoding known as \"v4\", as implemented by the JSON RPC method\n * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].\n *\n * _Available since v3.4._\n */\nabstract contract EIP712 {\n /* solhint-disable var-name-mixedcase */\n // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to\n // invalidate the cached domain separator if the chain id changes.\n bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;\n uint256 private immutable _CACHED_CHAIN_ID;\n address private immutable _CACHED_THIS;\n\n bytes32 private immutable _HASHED_NAME;\n bytes32 private immutable _HASHED_VERSION;\n bytes32 private immutable _TYPE_HASH;\n\n /* solhint-enable var-name-mixedcase */\n\n /**\n * @dev Initializes the domain separator and parameter caches.\n *\n * The meaning of `name` and `version` is specified in\n * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:\n *\n * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.\n * - `version`: the current major version of the signing domain.\n *\n * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart\n * contract upgrade].\n */\n constructor(string memory name, string memory version) {\n bytes32 hashedName = keccak256(bytes(name));\n bytes32 hashedVersion = keccak256(bytes(version));\n bytes32 typeHash = keccak256(\n \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"\n );\n _HASHED_NAME = hashedName;\n _HASHED_VERSION = hashedVersion;\n _CACHED_CHAIN_ID = block.chainid;\n _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);\n _CACHED_THIS = address(this);\n _TYPE_HASH = typeHash;\n }\n\n /**\n * @dev Returns the domain separator for the current chain.\n */\n function _domainSeparatorV4() internal view returns (bytes32) {\n if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {\n return _CACHED_DOMAIN_SEPARATOR;\n } else {\n return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);\n }\n }\n\n function _buildDomainSeparator(\n bytes32 typeHash,\n bytes32 nameHash,\n bytes32 versionHash\n ) private view returns (bytes32) {\n return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));\n }\n\n /**\n * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this\n * function returns the hash of the fully encoded EIP712 message for this domain.\n *\n * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:\n *\n * ```solidity\n * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(\n * keccak256(\"Mail(address to,string contents)\"),\n * mailTo,\n * keccak256(bytes(mailContents))\n * )));\n * address signer = ECDSA.recover(digest, signature);\n * ```\n */\n function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {\n return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/math/Math.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Down, // Toward negative infinity\n Up, // Toward infinity\n Zero // Toward zero\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a == 0 ? 0 : (a - 1) / b + 1;\n }\n\n /**\n * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n * with further edits by Uniswap Labs also under MIT license.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2^256 + prod0.\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(x, y, not(0))\n prod0 := mul(x, y)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division.\n if (prod1 == 0) {\n return prod0 / denominator;\n }\n\n // Make sure the result is less than 2^256. Also prevents denominator == 0.\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0].\n uint256 remainder;\n assembly {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n // See https://cs.stackexchange.com/q/138556/92363.\n\n // Does not overflow because the denominator cannot be zero at this stage in the function.\n uint256 twos = denominator & (~denominator + 1);\n assembly {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [prod1 prod0] by twos.\n prod0 := div(prod0, twos)\n\n // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from prod1 into prod0.\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv = 1 mod 2^4.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n // in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2^8\n inverse *= 2 - denominator * inverse; // inverse mod 2^16\n inverse *= 2 - denominator * inverse; // inverse mod 2^32\n inverse *= 2 - denominator * inverse; // inverse mod 2^64\n inverse *= 2 - denominator * inverse; // inverse mod 2^128\n inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inverse;\n return result;\n }\n }\n\n /**\n * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator,\n Rounding rounding\n ) internal pure returns (uint256) {\n uint256 result = mulDiv(x, y, denominator);\n if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n result += 1;\n }\n return result;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n *\n * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n }\n\n // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n //\n // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n //\n // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n //\n // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n uint256 result = 1 << (log2(a) >> 1);\n\n // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n // into the expected uint128 result.\n unchecked {\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n return min(result, a / result);\n }\n }\n\n /**\n * @notice Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 2, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 128;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 64;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 32;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 16;\n }\n if (value >> 8 > 0) {\n value >>= 8;\n result += 8;\n }\n if (value >> 4 > 0) {\n value >>= 4;\n result += 4;\n }\n if (value >> 2 > 0) {\n value >>= 2;\n result += 2;\n }\n if (value >> 1 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 10, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10**64) {\n value /= 10**64;\n result += 64;\n }\n if (value >= 10**32) {\n value /= 10**32;\n result += 32;\n }\n if (value >= 10**16) {\n value /= 10**16;\n result += 16;\n }\n if (value >= 10**8) {\n value /= 10**8;\n result += 8;\n }\n if (value >= 10**4) {\n value /= 10**4;\n result += 4;\n }\n if (value >= 10**2) {\n value /= 10**2;\n result += 2;\n }\n if (value >= 10**1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 256, rounded down, of a positive value.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 16;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 8;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 4;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 2;\n }\n if (value >> 8 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n }\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/math/SafeCast.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n *\n * _Available since v4.7._\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n require(value <= type(uint248).max, \"SafeCast: value doesn't fit in 248 bits\");\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n *\n * _Available since v4.7._\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n require(value <= type(uint240).max, \"SafeCast: value doesn't fit in 240 bits\");\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n *\n * _Available since v4.7._\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n require(value <= type(uint232).max, \"SafeCast: value doesn't fit in 232 bits\");\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n *\n * _Available since v4.2._\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n *\n * _Available since v4.7._\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n require(value <= type(uint216).max, \"SafeCast: value doesn't fit in 216 bits\");\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n *\n * _Available since v4.7._\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n require(value <= type(uint208).max, \"SafeCast: value doesn't fit in 208 bits\");\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n *\n * _Available since v4.7._\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n require(value <= type(uint200).max, \"SafeCast: value doesn't fit in 200 bits\");\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n *\n * _Available since v4.7._\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n require(value <= type(uint192).max, \"SafeCast: value doesn't fit in 192 bits\");\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n *\n * _Available since v4.7._\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n require(value <= type(uint184).max, \"SafeCast: value doesn't fit in 184 bits\");\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n *\n * _Available since v4.7._\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n require(value <= type(uint176).max, \"SafeCast: value doesn't fit in 176 bits\");\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n *\n * _Available since v4.7._\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n require(value <= type(uint168).max, \"SafeCast: value doesn't fit in 168 bits\");\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n *\n * _Available since v4.7._\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n require(value <= type(uint160).max, \"SafeCast: value doesn't fit in 160 bits\");\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n *\n * _Available since v4.7._\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n require(value <= type(uint152).max, \"SafeCast: value doesn't fit in 152 bits\");\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n *\n * _Available since v4.7._\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n require(value <= type(uint144).max, \"SafeCast: value doesn't fit in 144 bits\");\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n *\n * _Available since v4.7._\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n require(value <= type(uint136).max, \"SafeCast: value doesn't fit in 136 bits\");\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v2.5._\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n *\n * _Available since v4.7._\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n require(value <= type(uint120).max, \"SafeCast: value doesn't fit in 120 bits\");\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n *\n * _Available since v4.7._\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n require(value <= type(uint112).max, \"SafeCast: value doesn't fit in 112 bits\");\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n *\n * _Available since v4.7._\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n require(value <= type(uint104).max, \"SafeCast: value doesn't fit in 104 bits\");\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n *\n * _Available since v4.2._\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n *\n * _Available since v4.7._\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n require(value <= type(uint88).max, \"SafeCast: value doesn't fit in 88 bits\");\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n *\n * _Available since v4.7._\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n require(value <= type(uint80).max, \"SafeCast: value doesn't fit in 80 bits\");\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n *\n * _Available since v4.7._\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n require(value <= type(uint72).max, \"SafeCast: value doesn't fit in 72 bits\");\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v2.5._\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n *\n * _Available since v4.7._\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n require(value <= type(uint56).max, \"SafeCast: value doesn't fit in 56 bits\");\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n *\n * _Available since v4.7._\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n require(value <= type(uint48).max, \"SafeCast: value doesn't fit in 48 bits\");\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n *\n * _Available since v4.7._\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n require(value <= type(uint40).max, \"SafeCast: value doesn't fit in 40 bits\");\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v2.5._\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n *\n * _Available since v4.7._\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n require(value <= type(uint24).max, \"SafeCast: value doesn't fit in 24 bits\");\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v2.5._\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n *\n * _Available since v2.5._\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n *\n * _Available since v3.0._\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n *\n * _Available since v4.7._\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 248 bits\");\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n *\n * _Available since v4.7._\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 240 bits\");\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n *\n * _Available since v4.7._\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 232 bits\");\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n *\n * _Available since v4.7._\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 224 bits\");\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n *\n * _Available since v4.7._\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 216 bits\");\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n *\n * _Available since v4.7._\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 208 bits\");\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n *\n * _Available since v4.7._\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 200 bits\");\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n *\n * _Available since v4.7._\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 192 bits\");\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n *\n * _Available since v4.7._\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 184 bits\");\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n *\n * _Available since v4.7._\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 176 bits\");\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n *\n * _Available since v4.7._\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 168 bits\");\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n *\n * _Available since v4.7._\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 160 bits\");\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n *\n * _Available since v4.7._\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 152 bits\");\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n *\n * _Available since v4.7._\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 144 bits\");\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n *\n * _Available since v4.7._\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 136 bits\");\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 128 bits\");\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n *\n * _Available since v4.7._\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 120 bits\");\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n *\n * _Available since v4.7._\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 112 bits\");\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n *\n * _Available since v4.7._\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 104 bits\");\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n *\n * _Available since v4.7._\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 96 bits\");\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n *\n * _Available since v4.7._\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 88 bits\");\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n *\n * _Available since v4.7._\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 80 bits\");\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n *\n * _Available since v4.7._\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 72 bits\");\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 64 bits\");\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n *\n * _Available since v4.7._\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 56 bits\");\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n *\n * _Available since v4.7._\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 48 bits\");\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n *\n * _Available since v4.7._\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 40 bits\");\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 32 bits\");\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n *\n * _Available since v4.7._\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 24 bits\");\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 16 bits\");\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 8 bits\");\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n *\n * _Available since v3.0._\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n"
|
||
},
|
||
"@openzeppelin/contracts/utils/Strings.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n"
|
||
},
|
||
"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/addressCalcs.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @notice implements calculation of address for contracts deployed through CREATE.\n/// Accepts contract deployed from which address & nonce\nlibrary AddressCalcs {\n\n /// @notice Computes the address of a contract based\n /// @param deployedFrom_ Address from which the contract was deployed\n /// @param nonce_ Nonce at which the contract was deployed\n /// @return contract_ Address of deployed contract\n function calcAddress(address deployedFrom_, uint nonce_) internal pure returns (address contract_) {\n // @dev based on https://ethereum.stackexchange.com/a/61413\n\n // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment\n // hence, nonce of vault deployment starts with 1.\n bytes memory data;\n if (nonce_ == 0x00) {\n return address(0);\n } else if (nonce_ <= 0x7f) {\n data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployedFrom_, uint8(nonce_));\n } else if (nonce_ <= 0xff) {\n data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployedFrom_, bytes1(0x81), uint8(nonce_));\n } else if (nonce_ <= 0xffff) {\n data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployedFrom_, bytes1(0x82), uint16(nonce_));\n } else if (nonce_ <= 0xffffff) {\n data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployedFrom_, bytes1(0x83), uint24(nonce_));\n } else {\n data = abi.encodePacked(bytes1(0xda), bytes1(0x94), deployedFrom_, bytes1(0x84), uint32(nonce_));\n }\n\n return address(uint160(uint256(keccak256(data))));\n }\n\n}"
|
||
},
|
||
"contracts/libraries/bigMathMinified.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.\n/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision\n/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can\n/// result in significant gas cost reduction due to storage space reduction.\n/// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision.\n/// @dev roundUp is more like a increase 1, which happens everytime for the same number.\n/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number.\nlibrary BigMathMinified {\n /// @dev constants to use for `roundUp` input param to increase readability\n bool internal constant ROUND_DOWN = false;\n bool internal constant ROUND_UP = true;\n\n /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).\n /// e.g.:\n /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]\n /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)\n /// => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000\n /// ^-------------------- 51(exponent) -------------- ^\n /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563)\n /// exponent = 0011,0011 (51)\n /// bigNumber = 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 (572493200179)\n ///\n /// @param normal number which needs to be converted into Big Number\n /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))\n /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))\n /// @param roundUp signals if result should be rounded down or up\n /// @return bigNumber converted bigNumber (coefficient << exponent)\n function toBigNumber(\n uint256 normal,\n uint256 coefficientSize,\n uint256 exponentSize,\n bool roundUp\n ) internal pure returns (uint256 bigNumber) {\n assembly {\n let lastBit_\n let number_ := normal\n if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit_ := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit_ := add(lastBit_, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit_ := add(lastBit_, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit_ := add(lastBit_, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit_ := add(lastBit_, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit_ := add(lastBit_, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit_ := add(lastBit_, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit_ := add(lastBit_, 1)\n }\n if gt(number_, 0) {\n lastBit_ := add(lastBit_, 1)\n }\n if lt(lastBit_, coefficientSize) {\n // for throw exception\n lastBit_ := coefficientSize\n }\n let exponent := sub(lastBit_, coefficientSize)\n let coefficient := shr(exponent, normal)\n if and(roundUp, gt(exponent, 0)) {\n // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number\n coefficient := add(coefficient, 1)\n if eq(shl(coefficientSize, 1), coefficient) {\n // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits\n // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.\n coefficient := shl(sub(coefficientSize, 1), 1)\n exponent := add(exponent, 1)\n }\n }\n if iszero(lt(exponent, shl(exponentSize, 1))) {\n // if exponent is >= exponentSize, the normal number is too big to fit within\n // BigNumber with too small sizes for coefficient and exponent\n revert(0, 0)\n }\n bigNumber := shl(exponentSize, coefficient)\n bigNumber := add(bigNumber, exponent)\n }\n }\n\n /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`\n function fromBigNumber(\n uint256 bigNumber,\n uint256 exponentSize,\n uint256 exponentMask\n ) internal pure returns (uint256 normal) {\n assembly {\n let coefficient := shr(exponentSize, bigNumber)\n let exponent := and(bigNumber, exponentMask)\n normal := shl(exponent, coefficient)\n }\n }\n\n /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).\n /// e.g.\n /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111\n /// lastBit = ^--------------------------------- 83 ----------------------------------------^\n function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {\n assembly {\n let number_ := normal\n if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit := add(lastBit, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit := add(lastBit, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit := add(lastBit, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit := add(lastBit, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit := add(lastBit, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit := add(lastBit, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit := add(lastBit, 1)\n }\n if gt(number_, 0) {\n lastBit := add(lastBit, 1)\n }\n }\n }\n}\n"
|
||
},
|
||
"contracts/libraries/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<<precision_)/coe2>>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<<precision_)/coe2, exp1+decimal-exp2-precision_)\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 /// Returned connection factor can only ever be >= baseBranchDebtFactor (c = x*100/y with both x,y > 0 & x,y <= 100: c can only ever be >= x)\n function divBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {\n unchecked {\n // (coefficient1_ << PRECISION) / coefficient2_\n uint256 resCoefficient_ = ((bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) << PRECISION) /\n (bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR);\n // nominator at min 17179869184 << 64 = 316912650057057350374175801344. at max 34359738367 << 64 = 633825300095667956674642051072.\n // so min value resCoefficient_ 9223372037123211264 (64 bits) vs max 36893488146345361408 (fits in 65 bits)\n\n // mostSigBit will be PRECISION + 1 or PRECISION\n uint256 overflowLen_ = ((resCoefficient_ >> PRECISION) == 1) ? (PRECISION + 1) : PRECISION;\n // Overflow will be PRECISION - COEFFICIENT_SIZE_DEBT_FACTOR or (PRECISION + 1) - COEFFICIENT_SIZE_DEBT_FACTOR\n // Meaning 64 - 35 = 29 or 65 - 35 = 30\n overflowLen_ = overflowLen_ - COEFFICIENT_SIZE_DEBT_FACTOR;\n resCoefficient_ = resCoefficient_ >> overflowLen_;\n\n // exponent1_ will always be less than or equal to 16384\n // exponent2_ will always be less than or equal to 16384\n // Even if exponent2_ is 0 (not possible) & resExponent_ = DECIMALS_DEBT_FACTOR then also resExponent_ will be less than max limit, so no overflow\n // result exponent = (exponent1_ + DECIMALS_DEBT_FACTOR + overflowLen_) - (exponent2_ + PRECISION);\n uint256 resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) + // exponent1_\n DECIMALS_DEBT_FACTOR + // DECIMALS_DEBT_FACTOR is 100% as it is percentage value\n overflowLen_); // addition part resExponent_ here min 16414, max 32798\n // reuse overFlowLen_ variable for subtraction sum of exponent\n overflowLen_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) + PRECISION; // subtraction part overflowLen_ here: min 65, max 16448\n if (resExponent_ > overflowLen_) {\n resExponent_ = resExponent_ - overflowLen_;\n\n return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_);\n }\n\n // Can happen if bigNumber1 exponent is < 35 (35+16384+29 = 16448) and bigNumber2 exponent is e.g. max 16384.\n // this would mean a branch with a normal big debt factor (bigNumber2) is merged into a base branch with an extremely small\n // debt factor (bigNumber1).\n // this should never happen anyway, but if it does better to revert than to continue with unknown effects.\n revert(); // connection factor should never become a BigNumber with exponent <= 0\n }\n }\n}\n"
|
||
},
|
||
"contracts/libraries/errorTypes.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary LibsErrorTypes {\n /***********************************|\n | LiquidityCalcs | \n |__________________________________*/\n\n /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)\n uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;\n\n /// @notice thrown when rate data is set to a version that is not implemented\n uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;\n\n /// @notice thrown when the calculated borrow rate turns negative. This should never happen.\n uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003;\n\n /***********************************|\n | SafeTransfer | \n |__________________________________*/\n\n /// @notice thrown when safe transfer from for an ERC20 fails\n uint256 internal constant SafeTransfer__TransferFromFailed = 71001;\n\n /// @notice thrown when safe transfer for an ERC20 fails\n uint256 internal constant SafeTransfer__TransferFailed = 71002;\n}\n"
|
||
},
|
||
"contracts/libraries/liquidityCalcs.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { LibsErrorTypes as ErrorTypes } from \"./errorTypes.sol\";\nimport { LiquiditySlotsLink } from \"./liquiditySlotsLink.sol\";\nimport { BigMathMinified } from \"./bigMathMinified.sol\";\n\n/// @notice implements calculation methods used for Fluid liquidity such as updated exchange prices,\n/// borrow rate, withdrawal / borrow limits, revenue amount.\nlibrary LiquidityCalcs {\n error FluidLiquidityCalcsError(uint256 errorId_);\n\n /// @notice emitted if the calculated borrow rate surpassed max borrow rate (16 bits) and was capped at maximum value 65535\n event BorrowRateMaxCap();\n\n /// @dev constants as from Liquidity variables.sol\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n // constants used for BigMath conversion from and to storage\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;\n\n uint256 internal constant FOUR_DECIMALS = 1e4;\n uint256 internal constant TWELVE_DECIMALS = 1e12;\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X15 = 0x7fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n uint256 internal constant X33 = 0x1ffffffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC EXCHANGE PRICES /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage.\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @return supplyExchangePrice_ updated supplyExchangePrice\n /// @return borrowExchangePrice_ updated borrowExchangePrice\n function calcExchangePrices(\n uint256 exchangePricesAndConfig_\n ) internal view returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n // Extracting exchange prices\n supplyExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &\n X64;\n borrowExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &\n X64;\n\n if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__ExchangePriceZero);\n }\n\n uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate\n\n unchecked {\n // last timestamp can not be > current timestamp\n uint256 secondsSinceLastUpdate_ = block.timestamp -\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33);\n\n uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) &\n X15;\n if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) {\n // if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed\n // (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // calculate new borrow exchange price.\n // formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_.\n // nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0.\n borrowExchangePrice_ +=\n (borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS);\n\n // FOR SUPPLY EXCHANGE PRICE:\n // all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest.\n // formula: previous price * supply rate * secondsSinceLastUpdate_.\n // where supply rate = (borrow rate - revenueFee%) * ratioSupplyYield. And\n // ratioSupplyYield = utilization * supplyRatio * borrowRatio\n //\n // Example:\n // supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50.\n // BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%.\n // yield is 10 (so half a year must have passed).\n // supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60.\n // borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70.\n // supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2).\n // utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield).\n // borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield):\n // x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%.\n // ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5%\n // supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5%\n // increase in supplyExchangePrice, assuming 100 as previous price.\n // 100 * 22,5% * 1/2 (half a year) = 0,1125.\n // cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20.\n\n // -------------- 1. calculate ratioSupplyYield --------------------------------\n // step1: utilization * supplyRatio (or actually part of lenders receiving yield)\n\n // temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n // if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n // else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15;\n\n if (temp_ == 1) {\n // if no raw supply: no exchange price update needed\n // (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest\n if (temp_ & 1 == 1) {\n // ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = temp_ >> 1;\n\n // Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return\n // in the if statement a little above.\n\n // based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee.\n // supplyRawInterest must become worth 30. totalSupply must become 110.\n // supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2.\n // so ratioSupplyYield must come out as 2.5 (250%).\n // supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted.\n temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision)\n // e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%).\n temp_ =\n // utilization * (100% + 100% / supplyRatio)\n (((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) *\n (1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS);\n // max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31\n } else {\n // ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n temp_ = temp_ >> 1;\n // if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0\n\n // e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%).\n temp_ =\n // 1e27 * utilization * (100% + supplyRatio) / 100%\n (1e27 *\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS + temp_)) /\n (FOUR_DECIMALS * FOUR_DECIMALS);\n // max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27\n }\n // from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31\n\n // step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield)\n if (borrowRatio_ & 1 == 1) {\n // ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n\n // Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered\n // at the beginning of the method by early return if `borrowRatio_ == 1`.\n\n // based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%.\n // so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed.\n // x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666%\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_);\n // max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield).\n } else {\n // ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n // x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%.\n borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_)));\n // borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%.\n // or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101.\n // max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield).\n }\n\n // temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%.\n // max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8\n temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54;\n\n // 2. calculate supply rate\n // temp_ => supply rate (borrow rate - revenueFee%) * ratioSupplyYield.\n // division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate)\n // Note that all calculation divisions for supplyExchangePrice are rounded down.\n // Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest\n // but more suppliers not earning interest.\n temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate\n temp_ * // ratioSupplyYield\n (FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee\n // fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17.\n\n // 3. calculate increase in supply exchange price\n supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS));\n // max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0.\n }\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC REVENUE /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage\n /// and the current balance of the Fluid liquidity contract for the token.\n /// @param totalAmounts_ total amounts packed uint256 read from storage\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))\n /// @return revenueAmount_ collectable revenue amount\n function calcRevenue(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_\n ) internal view returns (uint256 revenueAmount_) {\n // @dev no need to super-optimize this method as it is only used by admin\n\n // calculate the new exchange prices based on earned interest\n (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(exchangePricesAndConfig_);\n\n // total supply = interest free + with interest converted from raw\n uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_);\n\n if (totalSupply_ > 0) {\n // available revenue: balanceOf(token) + totalBorrowings - totalLendings.\n revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_);\n // ensure there is no possible case because of rounding etc. where this would revert,\n // explicitly check if >\n revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0;\n // Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization\n // can only be revenue.\n } else {\n // if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck\n revenueAmount_ = liquidityTokenBalance_;\n }\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC LIMITS /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev calculates withdrawal limit before an operate execution:\n /// amount of user supply that must stay supplied (not amount that can be withdrawn).\n /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M\n /// @param userSupplyData_ user supply data packed uint256 from storage\n /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath\n /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction.\n /// returned value is in raw for with interest mode, normal amount for interest free mode!\n function calcWithdrawalLimitBeforeOperate(\n uint256 userSupplyData_,\n uint256 userSupply_\n ) internal view returns (uint256 currentWithdrawalLimit_) {\n // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet).\n // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet.\n // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be\n // a deposit anyway. Important is that it would not revert.\n\n // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit\n // is the fully expanded limit immediately.\n\n // extract last set withdrawal limit\n uint256 lastWithdrawalLimit_ = (userSupplyData_ >>\n LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) & X64;\n lastWithdrawalLimit_ =\n (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) <<\n (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK);\n if (lastWithdrawalLimit_ == 0) {\n // withdrawal limit is not activated. Max withdrawal allowed\n return 0;\n }\n\n uint256 maxWithdrawableLimit_;\n uint256 temp_;\n unchecked {\n // extract max withdrawable percent of user supply and\n // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed\n // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed.\n\n // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n maxWithdrawableLimit_ =\n (((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) /\n FOUR_DECIMALS;\n\n // time elapsed since last withdrawal limit was set (in seconds)\n // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before.\n // last timestamp can not be > current timestamp\n temp_ =\n block.timestamp -\n ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33);\n }\n // calculate withdrawable amount of expandPercent that is elapsed of expandDuration.\n // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%.\n // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed.\n temp_ =\n (maxWithdrawableLimit_ * temp_) /\n // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit)\n ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0\n // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount.\n // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration,\n // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0\n // which will cause minimum (fully expanded) withdrawal limit to be set in lines below.\n unchecked {\n // underflow explicitly checked & handled\n currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0;\n // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion.\n // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_\n temp_ = userSupply_ - maxWithdrawableLimit_;\n }\n // if withdrawal limit is decreased below minimum then set minimum\n // (e.g. when more than expandDuration time has elapsed)\n if (temp_ > currentWithdrawalLimit_) {\n currentWithdrawalLimit_ = temp_;\n }\n }\n\n /// @dev calculates withdrawal limit after an operate execution:\n /// amount of user supply that must stay supplied (not amount that can be withdrawn).\n /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M\n /// @param userSupplyData_ user supply data packed uint256 from storage\n /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount\n /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate`\n /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in\n /// raw for with interest mode, normal amount for interest free mode!\n function calcWithdrawalLimitAfterOperate(\n uint256 userSupplyData_,\n uint256 userSupply_,\n uint256 newWithdrawalLimit_\n ) internal pure returns (uint256) {\n // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed\n uint256 temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n // if user supply is below base limit then max withdrawals are allowed\n if (userSupply_ < temp_) {\n return 0;\n }\n // temp_ => withdrawal limit expandPercent (is in 1e2 decimals)\n temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;\n unchecked {\n // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent))\n // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_\n temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS);\n }\n // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit.\n // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where\n // increased deposit amount outpaces withrawals.\n if (temp_ > newWithdrawalLimit_) {\n return temp_;\n }\n return newWithdrawalLimit_;\n }\n\n /// @dev calculates borrow limit before an operate execution:\n /// total amount user borrow can reach (not borrowable amount in current operation).\n /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M\n /// @param userBorrowData_ user borrow data packed uint256 from storage\n /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_`\n /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in\n /// raw for with interest mode, normal amount for interest free mode!\n function calcBorrowLimitBeforeOperate(\n uint256 userBorrowData_,\n uint256 userBorrow_\n ) internal view returns (uint256 currentBorrowLimit_) {\n // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit.\n // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus\n // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0.\n\n // temp_ = extract borrow expand percent (is in 1e2 decimals)\n uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;\n\n uint256 maxExpansionLimit_;\n uint256 maxExpandedBorrowLimit_;\n unchecked {\n // calculate max expansion limit: Max amount limit can expand to since last interaction\n // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS);\n\n // calculate max borrow limit: Max point limit can increase to since last interaction\n maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_;\n }\n\n // currentBorrowLimit_ = extract base borrow limit\n currentBorrowLimit_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;\n currentBorrowLimit_ =\n (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) <<\n (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK);\n\n if (maxExpandedBorrowLimit_ < currentBorrowLimit_) {\n return currentBorrowLimit_;\n }\n // time elapsed since last borrow limit was set (in seconds)\n unchecked {\n // temp_ = timeElapsed_ (last timestamp can not be > current timestamp)\n temp_ =\n block.timestamp -\n ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last update timestamp\n }\n\n // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit\n currentBorrowLimit_ =\n // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`.\n // divisor is extract expand duration (after this, full expansion to expandPercentage happened).\n ((maxExpansionLimit_ * temp_) /\n ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0\n // extract last set borrow limit\n BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion,\n // so set to `maxExpandedBorrowLimit_` in that case.\n // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big)\n if (currentBorrowLimit_ > maxExpandedBorrowLimit_) {\n currentBorrowLimit_ = maxExpandedBorrowLimit_;\n }\n // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n if (currentBorrowLimit_ > temp_) {\n currentBorrowLimit_ = temp_;\n }\n }\n\n /// @dev calculates borrow limit after an operate execution:\n /// total amount user borrow can reach (not borrowable amount in current operation).\n /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M\n /// @param userBorrowData_ user borrow data packed uint256 from storage\n /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount\n /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate`\n /// @return borrowLimit_ updated borrow limit that should be written to storage.\n /// returned value is in raw for with interest mode, normal amount for interest free mode!\n function calcBorrowLimitAfterOperate(\n uint256 userBorrowData_,\n uint256 userBorrow_,\n uint256 newBorrowLimit_\n ) internal pure returns (uint256 borrowLimit_) {\n // temp_ = extract borrow expand percent\n uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals)\n\n unchecked {\n // borrowLimit_ = calculate maximum borrow limit at full expansion.\n // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS);\n }\n\n // temp_ = extract base borrow limit\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n if (borrowLimit_ < temp_) {\n // below base limit, borrow limit is always base limit\n return temp_;\n }\n // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n // make sure fully expanded borrow limit is not above hard max borrow limit\n if (borrowLimit_ > temp_) {\n borrowLimit_ = temp_;\n }\n // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit.\n // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)\n if (newBorrowLimit_ > borrowLimit_) {\n return borrowLimit_;\n }\n return newBorrowLimit_;\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC RATES /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev Calculates new borrow rate from utilization for a token\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ totalBorrow / totalSupply. 1e4 = 100% utilization\n /// @return rate_ rate for that particular token in 1e2 precision (e.g. 5% rate = 500)\n function calcBorrowRateFromUtilization(uint256 rateData_, uint256 utilization_) internal returns (uint256 rate_) {\n // extract rate version: 4 bits (0xF) starting from bit 0\n uint256 rateVersion_ = (rateData_ & 0xF);\n\n if (rateVersion_ == 1) {\n rate_ = calcRateV1(rateData_, utilization_);\n } else if (rateVersion_ == 2) {\n rate_ = calcRateV2(rateData_, utilization_);\n } else {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__UnsupportedRateVersion);\n }\n\n if (rate_ > X16) {\n // hard cap for borrow rate at maximum value 16 bits (65535) to make sure it does not overflow storage space.\n // this is unlikely to ever happen if configs stay within expected levels.\n rate_ = X16;\n // emit event to more easily become aware\n emit BorrowRateMaxCap();\n }\n }\n\n /// @dev calculates the borrow rate based on utilization for rate data version 1 (with one kink) in 1e2 precision\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ in 1e2 (100% = 1e4)\n /// @return rate_ rate in 1e2 precision\n function calcRateV1(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {\n /// For rate v1 (one kink) ------------------------------------------------------\n /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 52- 67 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Last 188 bits => 68-255 => blank, might come in use in future\n\n // y = mx + c.\n // y is borrow rate\n // x is utilization\n // m = slope (m can also be negative for declining rates)\n // c is constant (c can be negative)\n\n uint256 y1_;\n uint256 y2_;\n uint256 x1_;\n uint256 x2_;\n\n // extract kink1: 16 bits (0xFFFF) starting from bit 20\n // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two\n uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16;\n if (utilization_ < kink1_) {\n // if utilization is less than kink\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;\n x1_ = 0; // 0%\n x2_ = kink1_;\n } else {\n // else utilization is greater than kink\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) & X16;\n x1_ = kink1_;\n x2_ = FOUR_DECIMALS; // 100%\n }\n\n int256 constant_;\n int256 slope_;\n unchecked {\n // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).\n // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor)\n // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS\n slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_));\n\n // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.\n // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256\n // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;\n // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256\n // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256\n constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_));\n\n // calculating new borrow rate\n // - slope_ max value is 65535 * 1e12,\n // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)\n // - constant max value is 65535 * 1e12\n // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256\n // divisor TWELVE_DECIMALS can not be 0\n slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings\n if (slope_ < 0) {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative);\n }\n rate_ = uint256(slope_) / TWELVE_DECIMALS;\n }\n }\n\n /// @dev calculates the borrow rate based on utilization for rate data version 2 (with two kinks) in 1e4 precision\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ in 1e2 (100% = 1e4)\n /// @return rate_ rate in 1e4 precision\n function calcRateV2(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {\n /// For rate v2 (two kinks) -----------------------------------------------------\n /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 52- 67 => Utilization at kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 68- 83 => Rate at utilization kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 84- 99 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Last 156 bits => 100-255 => blank, might come in use in future\n\n // y = mx + c.\n // y is borrow rate\n // x is utilization\n // m = slope (m can also be negative for declining rates)\n // c is constant (c can be negative)\n\n uint256 y1_;\n uint256 y2_;\n uint256 x1_;\n uint256 x2_;\n\n // extract kink1: 16 bits (0xFFFF) starting from bit 20\n // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two\n uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) & X16;\n if (utilization_ < kink1_) {\n // if utilization is less than kink1\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;\n x1_ = 0; // 0%\n x2_ = kink1_;\n } else {\n // extract kink2: 16 bits (0xFFFF) starting from bit 52\n uint256 kink2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) & X16;\n if (utilization_ < kink2_) {\n // if utilization is less than kink2\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;\n x1_ = kink1_;\n x2_ = kink2_;\n } else {\n // else utilization is greater than kink2\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) & X16;\n x1_ = kink2_;\n x2_ = FOUR_DECIMALS;\n }\n }\n\n int256 constant_;\n int256 slope_;\n unchecked {\n // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).\n // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor)\n // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS\n slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_));\n\n // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.\n // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256\n // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;\n // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256\n // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256\n constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_));\n\n // calculating new borrow rate\n // - slope_ max value is 65535 * 1e12,\n // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)\n // - constant max value is 65535 * 1e12\n // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256\n // divisor TWELVE_DECIMALS can not be 0\n slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings\n if (slope_ < 0) {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative);\n }\n rate_ = uint256(slope_) / TWELVE_DECIMALS;\n }\n }\n\n /// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_`\n function getTotalSupply(\n uint256 totalAmounts_,\n uint256 supplyExchangePrice_\n ) internal pure returns (uint256 totalSupply_) {\n // totalSupply_ => supplyInterestFree\n totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;\n totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits\n totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalSupply = supplyInterestFree + supplyRawInterest normalized from raw\n totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n\n /// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_`\n function getTotalBorrow(\n uint256 totalAmounts_,\n uint256 borrowExchangePrice_\n ) internal pure returns (uint256 totalBorrow_) {\n // totalBorrow_ => borrowInterestFree\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);\n totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;\n totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw\n totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n}\n"
|
||
},
|
||
"contracts/libraries/liquiditySlotsLink.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @notice library that helps in reading / working with storage slot data of Fluid Liquidity.\n/// @dev as all data for Fluid Liquidity is internal, any data must be fetched directly through manual\n/// slot reading through this library or, if gas usage is less important, through the FluidLiquidityResolver.\nlibrary LiquiditySlotsLink {\n /// @dev storage slot for status at Liquidity\n uint256 internal constant LIQUIDITY_STATUS_SLOT = 1;\n /// @dev storage slot for auths mapping at Liquidity\n uint256 internal constant LIQUIDITY_AUTHS_MAPPING_SLOT = 2;\n /// @dev storage slot for guardians mapping at Liquidity\n uint256 internal constant LIQUIDITY_GUARDIANS_MAPPING_SLOT = 3;\n /// @dev storage slot for user class mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_CLASS_MAPPING_SLOT = 4;\n /// @dev storage slot for exchangePricesAndConfig mapping at Liquidity\n uint256 internal constant LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT = 5;\n /// @dev storage slot for rateData mapping at Liquidity\n uint256 internal constant LIQUIDITY_RATE_DATA_MAPPING_SLOT = 6;\n /// @dev storage slot for totalAmounts mapping at Liquidity\n uint256 internal constant LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT = 7;\n /// @dev storage slot for user supply double mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT = 8;\n /// @dev storage slot for user borrow double mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT = 9;\n /// @dev storage slot for listed tokens array at Liquidity\n uint256 internal constant LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT = 10;\n /// @dev storage slot for listed tokens array at Liquidity\n uint256 internal constant LIQUIDITY_CONFIGS2_MAPPING_SLOT = 11;\n\n // --------------------------------\n // @dev stacked uint256 storage slots bits position data for each:\n\n // ExchangePricesAndConfig\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATE = 0;\n uint256 internal constant BITS_EXCHANGE_PRICES_FEE = 16;\n uint256 internal constant BITS_EXCHANGE_PRICES_UTILIZATION = 30;\n uint256 internal constant BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD = 44;\n uint256 internal constant BITS_EXCHANGE_PRICES_LAST_TIMESTAMP = 58;\n uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE = 91;\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE = 155;\n uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_RATIO = 219;\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATIO = 234;\n uint256 internal constant BITS_EXCHANGE_PRICES_USES_CONFIGS2 = 249;\n\n // RateData:\n uint256 internal constant BITS_RATE_DATA_VERSION = 0;\n // RateData: V1\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO = 4;\n uint256 internal constant BITS_RATE_DATA_V1_UTILIZATION_AT_KINK = 20;\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK = 36;\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX = 52;\n // RateData: V2\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO = 4;\n uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1 = 20;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1 = 36;\n uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2 = 52;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2 = 68;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX = 84;\n\n // TotalAmounts\n uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_WITH_INTEREST = 0;\n uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE = 64;\n uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST = 128;\n uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE = 192;\n\n // UserSupplyData\n uint256 internal constant BITS_USER_SUPPLY_MODE = 0;\n uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;\n uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;\n uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;\n uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;\n uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;\n uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;\n uint256 internal constant BITS_USER_SUPPLY_IS_PAUSED = 255;\n\n // UserBorrowData\n uint256 internal constant BITS_USER_BORROW_MODE = 0;\n uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;\n uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;\n uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;\n uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;\n uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;\n uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;\n uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;\n uint256 internal constant BITS_USER_BORROW_IS_PAUSED = 255;\n\n // Configs2\n uint256 internal constant BITS_CONFIGS2_MAX_UTILIZATION = 0;\n\n // --------------------------------\n\n /// @notice Calculating the slot ID for Liquidity contract for single mapping at `slot_` for `key_`\n function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for double mapping at `slot_` for `key1_` and `key2_`\n function calculateDoubleMappingStorageSlot(\n uint256 slot_,\n address key1_,\n address key2_\n ) internal pure returns (bytes32) {\n bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));\n return keccak256(abi.encode(key2_, intermediateSlot_));\n }\n}\n"
|
||
},
|
||
"contracts/libraries/safeTransfer.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT OR Apache-2.0\npragma solidity 0.8.21;\n\nimport { LibsErrorTypes as ErrorTypes } from \"./errorTypes.sol\";\n\n/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom\nlibrary SafeTransfer {\n error FluidSafeTransferError(uint256 errorId_);\n\n /// @dev Transfer `amount_` of `token_` from `from_` to `to_`, spending the approval given by `from_` to the\n /// calling contract. If `token_` returns no value, non-reverting calls are assumed to be successful.\n /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63\n function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"from_\" argument.\n mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"to_\" argument.\n mstore(add(freeMemoryPointer, 68), amount_) // Append the \"amount_\" argument. Masking not required as it's a full 32 byte type.\n\n success_ := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)\n )\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);\n }\n }\n\n /// @dev Transfer `amount_` of `token_` to `to_`.\n /// If `token_` returns no value, non-reverting calls are assumed to be successful.\n /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95\n function safeTransfer(address token_, address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"to_\" argument.\n mstore(add(freeMemoryPointer, 36), amount_) // Append the \"amount_\" argument. Masking not required as it's a full 32 byte type.\n\n success_ := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);\n }\n }\n\n /// @dev Transfer `amount_` of ` native token to `to_`.\n /// Minimally modified from Solmate SafeTransferLib (Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25\n function safeTransferNative(address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Transfer the ETH and store if it succeeded or not.\n success_ := call(gas(), to_, amount_, 0, 0, 0, 0)\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);\n }\n }\n}\n"
|
||
},
|
||
"contracts/libraries/storageRead.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @notice implements a method to read uint256 data from storage at a bytes32 storage slot key.\ncontract StorageRead {\n function readFromStorage(bytes32 slot_) public view returns (uint256 result_) {\n assembly {\n result_ := sload(slot_) // read value from the storage slot\n }\n }\n}\n"
|
||
},
|
||
"contracts/libraries/tickMath.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title library that calculates number \"tick\" and \"ratioX96\" from this: ratioX96 = (1.0015^tick) * 2^96\n/// @notice this library is used in Fluid Vault protocol for optimiziation.\n/// @dev \"tick\" supports between -32767 and 32767. \"ratioX96\" supports between 37075072 and 169307877264527972847801929085841449095838922544595\nlibrary TickMath {\n /// The minimum tick that can be passed in getRatioAtTick. 1.0015**-32767\n int24 internal constant MIN_TICK = -32767;\n /// The maximum tick that can be passed in getRatioAtTick. 1.0015**32767\n int24 internal constant MAX_TICK = 32767;\n\n uint256 internal constant FACTOR00 = 0x100000000000000000000000000000000;\n uint256 internal constant FACTOR01 = 0xff9dd7de423466c20352b1246ce4856f; // 2^128/1.0015**1 = 339772707859149738855091969477551883631\n uint256 internal constant FACTOR02 = 0xff3bd55f4488ad277531fa1c725a66d0; // 2^128/1.0015**2 = 339263812140938331358054887146831636176\n uint256 internal constant FACTOR03 = 0xfe78410fd6498b73cb96a6917f853259; // 2^128/1.0015**4 = 338248306163758188337119769319392490073\n uint256 internal constant FACTOR04 = 0xfcf2d9987c9be178ad5bfeffaa123273; // 2^128/1.0015**8 = 336226404141693512316971918999264834163\n uint256 internal constant FACTOR05 = 0xf9ef02c4529258b057769680fc6601b3; // 2^128/1.0015**16 = 332218786018727629051611634067491389875\n uint256 internal constant FACTOR06 = 0xf402d288133a85a17784a411f7aba082; // 2^128/1.0015**32 = 324346285652234375371948336458280706178\n uint256 internal constant FACTOR07 = 0xe895615b5beb6386553757b0352bda90; // 2^128/1.0015**64 = 309156521885964218294057947947195947664\n uint256 internal constant FACTOR08 = 0xd34f17a00ffa00a8309940a15930391a; // 2^128/1.0015**128 = 280877777739312896540849703637713172762 \n uint256 internal constant FACTOR09 = 0xae6b7961714e20548d88ea5123f9a0ff; // 2^128/1.0015**256 = 231843708922198649176471782639349113087\n uint256 internal constant FACTOR10 = 0x76d6461f27082d74e0feed3b388c0ca1; // 2^128/1.0015**512 = 157961477267171621126394973980180876449\n uint256 internal constant FACTOR11 = 0x372a3bfe0745d8b6b19d985d9a8b85bb; // 2^128/1.0015**1024 = 73326833024599564193373530205717235131\n uint256 internal constant FACTOR12 = 0x0be32cbee48979763cf7247dd7bb539d; // 2^128/1.0015**2048 = 15801066890623697521348224657638773661\n uint256 internal constant FACTOR13 = 0x8d4f70c9ff4924dac37612d1e2921e; // 2^128/1.0015**4096 = 733725103481409245883800626999235102\n uint256 internal constant FACTOR14 = 0x4e009ae5519380809a02ca7aec77; // 2^128/1.0015**8192 = 1582075887005588088019997442108535\n uint256 internal constant FACTOR15 = 0x17c45e641b6e95dee056ff10; // 2^128/1.0015**16384 = 7355550435635883087458926352\n\n /// The minimum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MIN_TICK). ~ Equivalent to `(1 << 96) * (1.0015**-32767)`\n uint256 internal constant MIN_RATIOX96 = 37075072;\n /// The maximum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MAX_TICK).\n /// ~ Equivalent to `(1 << 96) * (1.0015**32767)`, rounding etc. leading to minor difference\n uint256 internal constant MAX_RATIOX96 = 169307877264527972847801929085841449095838922544595;\n\n uint256 internal constant ZERO_TICK_SCALED_RATIO = 0x1000000000000000000000000; // 1 << 96 // 79228162514264337593543950336\n uint256 internal constant _1E26 = 1e26;\n\n /// @notice ratioX96 = (1.0015^tick) * 2^96\n /// @dev Throws if |tick| > max tick\n /// @param tick The input tick for the above formula\n /// @return ratioX96 ratio = (debt amount/collateral amount)\n function getRatioAtTick(int tick) internal pure returns (uint256 ratioX96) {\n assembly {\n let absTick_ := sub(xor(tick, sar(255, tick)), sar(255, tick))\n\n if gt(absTick_, MAX_TICK) {\n revert(0, 0)\n }\n let factor_ := FACTOR00\n if and(absTick_, 0x1) {\n factor_ := FACTOR01\n }\n if and(absTick_, 0x2) {\n factor_ := shr(128, mul(factor_, FACTOR02))\n }\n if and(absTick_, 0x4) {\n factor_ := shr(128, mul(factor_, FACTOR03))\n }\n if and(absTick_, 0x8) {\n factor_ := shr(128, mul(factor_, FACTOR04))\n }\n if and(absTick_, 0x10) {\n factor_ := shr(128, mul(factor_, FACTOR05))\n }\n if and(absTick_, 0x20) {\n factor_ := shr(128, mul(factor_, FACTOR06))\n }\n if and(absTick_, 0x40) {\n factor_ := shr(128, mul(factor_, FACTOR07))\n }\n if and(absTick_, 0x80) {\n factor_ := shr(128, mul(factor_, FACTOR08))\n }\n if and(absTick_, 0x100) {\n factor_ := shr(128, mul(factor_, FACTOR09))\n }\n if and(absTick_, 0x200) {\n factor_ := shr(128, mul(factor_, FACTOR10))\n }\n if and(absTick_, 0x400) {\n factor_ := shr(128, mul(factor_, FACTOR11))\n }\n if and(absTick_, 0x800) {\n factor_ := shr(128, mul(factor_, FACTOR12))\n }\n if and(absTick_, 0x1000) {\n factor_ := shr(128, mul(factor_, FACTOR13))\n }\n if and(absTick_, 0x2000) {\n factor_ := shr(128, mul(factor_, FACTOR14))\n }\n if and(absTick_, 0x4000) {\n factor_ := shr(128, mul(factor_, FACTOR15))\n }\n\n let precision_ := 0\n if iszero(and(tick, 0x8000000000000000000000000000000000000000000000000000000000000000)) {\n factor_ := div(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, factor_)\n // we round up in the division so getTickAtRatio of the output price is always consistent\n if mod(factor_, 0x100000000) {\n precision_ := 1\n }\n }\n ratioX96 := add(shr(32, factor_), precision_)\n }\n }\n\n /// @notice ratioX96 = (1.0015^tick) * 2^96\n /// @dev Throws if ratioX96 > max ratio || ratioX96 < min ratio\n /// @param ratioX96 The input ratio; ratio = (debt amount/collateral amount)\n /// @return tick The output tick for the above formula. Returns in round down form. if tick is 123.23 then 123, if tick is -123.23 then returns -124\n /// @return perfectRatioX96 perfect ratio for the above tick\n function getTickAtRatio(uint256 ratioX96) internal pure returns (int tick, uint perfectRatioX96) {\n assembly {\n if or(gt(ratioX96, MAX_RATIOX96), lt(ratioX96, MIN_RATIOX96)) {\n revert(0, 0)\n }\n\n let cond := lt(ratioX96, ZERO_TICK_SCALED_RATIO)\n let factor_\n\n if iszero(cond) {\n // if ratioX96 >= ZERO_TICK_SCALED_RATIO\n factor_ := div(mul(ratioX96, _1E26), ZERO_TICK_SCALED_RATIO)\n }\n if cond {\n // ratioX96 < ZERO_TICK_SCALED_RATIO\n factor_ := div(mul(ZERO_TICK_SCALED_RATIO, _1E26), ratioX96)\n }\n\n // put in https://www.wolframalpha.com/ whole equation: (1.0015^tick) * 2^96 * 10^26 / 79228162514264337593543950336\n\n // for tick = 16384\n // ratioX96 = (1.0015^16384) * 2^96 = 3665252098134783297721995888537077351735\n // 3665252098134783297721995888537077351735 * 10^26 / 79228162514264337593543950336 =\n // 4626198540796508716348404308345255985.06131964639489434655721\n if iszero(lt(factor_, 4626198540796508716348404308345255985)) {\n tick := or(tick, 0x4000)\n factor_ := div(mul(factor_, _1E26), 4626198540796508716348404308345255985)\n }\n // for tick = 8192\n // ratioX96 = (1.0015^8192) * 2^96 = 17040868196391020479062776466509865\n // 17040868196391020479062776466509865 * 10^26 / 79228162514264337593543950336 =\n // 21508599537851153911767490449162.3037648642153898377655505172\n if iszero(lt(factor_, 21508599537851153911767490449162)) {\n tick := or(tick, 0x2000)\n factor_ := div(mul(factor_, _1E26), 21508599537851153911767490449162)\n }\n // for tick = 4096\n // ratioX96 = (1.0015^4096) * 2^96 = 36743933851015821532611831851150\n // 36743933851015821532611831851150 * 10^26 / 79228162514264337593543950336 =\n // 46377364670549310883002866648.9777607649742626173648716941385\n if iszero(lt(factor_, 46377364670549310883002866649)) {\n tick := or(tick, 0x1000)\n factor_ := div(mul(factor_, _1E26), 46377364670549310883002866649)\n }\n // for tick = 2048\n // ratioX96 = (1.0015^2048) * 2^96 = 1706210527034005899209104452335\n // 1706210527034005899209104452335 * 10^26 / 79228162514264337593543950336 =\n // 2153540449365864845468344760.06357108484096046743300420319322\n if iszero(lt(factor_, 2153540449365864845468344760)) {\n tick := or(tick, 0x800)\n factor_ := div(mul(factor_, _1E26), 2153540449365864845468344760)\n }\n // for tick = 1024\n // ratioX96 = (1.0015^1024) * 2^96 = 367668226692760093024536487236\n // 367668226692760093024536487236 * 10^26 / 79228162514264337593543950336 =\n // 464062544207767844008185024.950588990554136265212906454481127\n if iszero(lt(factor_, 464062544207767844008185025)) {\n tick := or(tick, 0x400)\n factor_ := div(mul(factor_, _1E26), 464062544207767844008185025)\n }\n // for tick = 512\n // ratioX96 = (1.0015^512) * 2^96 = 170674186729409605620119663668\n // 170674186729409605620119663668 * 10^26 / 79228162514264337593543950336 =\n // 215421109505955298802281577.031879604792139232258508172947569\n if iszero(lt(factor_, 215421109505955298802281577)) {\n tick := or(tick, 0x200)\n factor_ := div(mul(factor_, _1E26), 215421109505955298802281577)\n }\n // for tick = 256\n // ratioX96 = (1.0015^256) * 2^96 = 116285004205991934861656513301\n // 116285004205991934861656513301 * 10^26 / 79228162514264337593543950336 =\n // 146772309890508740607270614.667650899656438875541505058062410\n if iszero(lt(factor_, 146772309890508740607270615)) {\n tick := or(tick, 0x100)\n factor_ := div(mul(factor_, _1E26), 146772309890508740607270615)\n }\n // for tick = 128\n // ratioX96 = (1.0015^128) * 2^96 = 95984619659632141743747099590\n // 95984619659632141743747099590 * 10^26 / 79228162514264337593543950336 =\n // 121149622323187099817270416.157248837742741760456796835775887\n if iszero(lt(factor_, 121149622323187099817270416)) {\n tick := or(tick, 0x80)\n factor_ := div(mul(factor_, _1E26), 121149622323187099817270416)\n }\n // for tick = 64\n // ratioX96 = (1.0015^64) * 2^96 = 87204845308406958006717891124\n // 87204845308406958006717891124 * 10^26 / 79228162514264337593543950336 =\n // 110067989135437147685980801.568068573422377364214113968609839\n if iszero(lt(factor_, 110067989135437147685980801)) {\n tick := or(tick, 0x40)\n factor_ := div(mul(factor_, _1E26), 110067989135437147685980801)\n }\n // for tick = 32\n // ratioX96 = (1.0015^32) * 2^96 = 83120873769022354029916374475\n // 83120873769022354029916374475 * 10^26 / 79228162514264337593543950336 =\n // 104913292358707887270979599.831816586773651266562785765558183\n if iszero(lt(factor_, 104913292358707887270979600)) {\n tick := or(tick, 0x20)\n factor_ := div(mul(factor_, _1E26), 104913292358707887270979600)\n }\n // for tick = 16\n // ratioX96 = (1.0015^16) * 2^96 = 81151180492336368327184716176\n // 81151180492336368327184716176 * 10^26 / 79228162514264337593543950336 =\n // 102427189924701091191840927.762844039579442328381455567932128\n if iszero(lt(factor_, 102427189924701091191840928)) {\n tick := or(tick, 0x10)\n factor_ := div(mul(factor_, _1E26), 102427189924701091191840928)\n }\n // for tick = 8\n // ratioX96 = (1.0015^8) * 2^96 = 80183906840906820640659903620\n // 80183906840906820640659903620 * 10^26 / 79228162514264337593543950336 =\n // 101206318935480056907421312.890625\n if iszero(lt(factor_, 101206318935480056907421313)) {\n tick := or(tick, 0x8)\n factor_ := div(mul(factor_, _1E26), 101206318935480056907421313)\n }\n // for tick = 4\n // ratioX96 = (1.0015^4) * 2^96 = 79704602139525152702959747603\n // 79704602139525152702959747603 * 10^26 / 79228162514264337593543950336 =\n // 100601351350506250000000000\n if iszero(lt(factor_, 100601351350506250000000000)) {\n tick := or(tick, 0x4)\n factor_ := div(mul(factor_, _1E26), 100601351350506250000000000)\n }\n // for tick = 2\n // ratioX96 = (1.0015^2) * 2^96 = 79466025265172787701084167660\n // 79466025265172787701084167660 * 10^26 / 79228162514264337593543950336 =\n // 100300225000000000000000000\n if iszero(lt(factor_, 100300225000000000000000000)) {\n tick := or(tick, 0x2)\n factor_ := div(mul(factor_, _1E26), 100300225000000000000000000)\n }\n // for tick = 1\n // ratioX96 = (1.0015^1) * 2^96 = 79347004758035734099934266261\n // 79347004758035734099934266261 * 10^26 / 79228162514264337593543950336 =\n // 100150000000000000000000000\n if iszero(lt(factor_, 100150000000000000000000000)) {\n tick := or(tick, 0x1)\n factor_ := div(mul(factor_, _1E26), 100150000000000000000000000)\n }\n if iszero(cond) {\n // if ratioX96 >= ZERO_TICK_SCALED_RATIO\n perfectRatioX96 := div(mul(ratioX96, _1E26), factor_)\n }\n if cond {\n // ratioX96 < ZERO_TICK_SCALED_RATIO\n tick := not(tick)\n perfectRatioX96 := div(mul(ratioX96, factor_), 100150000000000000000000000)\n }\n // perfect ratio should always be <= ratioX96\n // not sure if it can ever be bigger but better to have extra checks\n if gt(perfectRatioX96, ratioX96) {\n revert(0, 0)\n }\n }\n }\n}\n"
|
||
},
|
||
"contracts/liquidity/adminModule/structs.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Structs {\n struct AddressBool {\n address addr;\n bool value;\n }\n\n struct AddressUint256 {\n address addr;\n uint256 value;\n }\n\n /// @notice struct to set borrow rate data for version 1\n struct RateDataV1Params {\n ///\n /// @param token for rate data\n address token;\n ///\n /// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast\n uint256 kink;\n ///\n /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100\n /// i.e. constant minimum borrow rate\n /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)\n uint256 rateAtUtilizationZero;\n ///\n /// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700\n uint256 rateAtUtilizationKink;\n ///\n /// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500\n uint256 rateAtUtilizationMax;\n }\n\n /// @notice struct to set borrow rate data for version 2\n struct RateDataV2Params {\n ///\n /// @param token for rate data\n address token;\n ///\n /// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster\n uint256 kink1;\n ///\n /// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast\n uint256 kink2;\n ///\n /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100\n /// i.e. constant minimum borrow rate\n /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)\n uint256 rateAtUtilizationZero;\n ///\n /// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700\n uint256 rateAtUtilizationKink1;\n ///\n /// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200\n uint256 rateAtUtilizationKink2;\n ///\n /// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500\n uint256 rateAtUtilizationMax;\n }\n\n /// @notice struct to set token config\n struct TokenConfig {\n ///\n /// @param token address\n address token;\n ///\n /// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100\n uint256 fee;\n ///\n /// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100\n uint256 threshold;\n ///\n /// @param maxUtilization maximum allowed utilization. in 1e2: 100% = 10_000; 1% = 100\n /// set to 100% to disable and have default limit of 100% (avoiding SLOAD).\n uint256 maxUtilization;\n }\n\n /// @notice struct to set user supply & withdrawal config\n struct UserSupplyConfig {\n ///\n /// @param user address\n address user;\n ///\n /// @param token address\n address token;\n ///\n /// @param mode: 0 = without interest. 1 = with interest\n uint8 mode;\n ///\n /// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100\n /// Also used to calculate rate at which withdrawal limit should decrease (instant).\n uint256 expandPercent;\n ///\n /// @param expandDuration withdrawal limit expand duration in seconds.\n /// used to calculate rate together with expandPercent\n uint256 expandDuration;\n ///\n /// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 baseWithdrawalLimit;\n }\n\n /// @notice struct to set user borrow & payback config\n struct UserBorrowConfig {\n ///\n /// @param user address\n address user;\n ///\n /// @param token address\n address token;\n ///\n /// @param mode: 0 = without interest. 1 = with interest\n uint8 mode;\n ///\n /// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100\n /// Also used to calculate rate at which debt limit should decrease (instant).\n uint256 expandPercent;\n ///\n /// @param expandDuration debt limit expand duration in seconds.\n /// used to calculate rate together with expandPercent\n uint256 expandDuration;\n ///\n /// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling\n /// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 baseDebtCeiling;\n ///\n /// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 maxDebtCeiling;\n }\n}\n"
|
||
},
|
||
"contracts/liquidity/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/mocks/mockOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../oracle/fluidOracle.sol\";\n\n/// @notice Mock Oracle for testing\ncontract MockOracle is FluidOracle {\n uint256 public price;\n\n constructor() FluidOracle(\"someName\") {}\n\n // Price is in 1e27 decimals between 2 tokens.\n // For example: if 1 ETH = 2000 USDC, that means 1e18 of ETH = 2000 * 1e6 of USDC\n // debt per col = 2000 * 1e6 * 1e27 / 1e18;\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() external view override returns (uint256 exchangeRate_) {\n return price;\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return price;\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return price;\n }\n\n function setPrice(uint256 price_) external {\n price = price_;\n }\n}\n"
|
||
},
|
||
"contracts/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 | FluidOracleL2 | \n |__________________________________*/\n\n /// @notice thrown when sequencer on a L2 has an outage and grace period has not yet passed.\n uint256 internal constant FluidOracleL2__SequencerOutage = 60000;\n\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 | FluidOracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid info name is passed into a fluid oracle (e.g. not set or too long)\n uint256 internal constant FluidOracle__InvalidInfoName = 60010;\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 | Pendle Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant PendleOracle__InvalidParams = 60201;\n\n /// @notice thrown when the Pendle market Oracle has not been initialized yet\n uint256 internal constant PendleOracle__MarketNotInitialized = 60202;\n\n /// @notice thrown when the Pendle market does not have 18 decimals\n uint256 internal constant PendleOracle__MarketInvalidDecimals = 60203;\n\n /// @notice thrown when the Pendle market returns an unexpected price\n uint256 internal constant PendleOracle__InvalidPrice = 60204;\n\n /***********************************|\n | CLRS2UniV3CheckCLRSOracleL2 | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config\n uint256 internal constant CLRS2UniV3CheckCLRSOracleL2__ExchangeRateZero = 60301;\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\";\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { Error as OracleError } from \"./error.sol\";\n\n/// @title FluidOracle\n/// @notice Base contract that any Fluid Oracle must implement\nabstract contract FluidOracle is IFluidOracle, OracleError {\n /// @dev short helper string to easily identify the oracle. E.g. token symbols\n //\n // using a bytes32 because string can not be immutable.\n bytes32 private immutable _infoName;\n\n constructor(string memory infoName_) {\n if (bytes(infoName_).length > 32 || bytes(infoName_).length == 0) {\n revert FluidOracleError(ErrorTypes.FluidOracle__InvalidInfoName);\n }\n\n // convert string to bytes32\n bytes32 infoNameBytes32_;\n assembly {\n infoNameBytes32_ := mload(add(infoName_, 32))\n }\n _infoName = infoNameBytes32_;\n }\n\n /// @inheritdoc IFluidOracle\n function infoName() external view returns (string memory) {\n // convert bytes32 to string\n uint256 length_;\n while (length_ < 32 && _infoName[length_] != 0) {\n length_++;\n }\n bytes memory infoNameBytes_ = new bytes(length_);\n for (uint256 i; i < length_; i++) {\n infoNameBytes_[i] = _infoName[i];\n }\n return string(infoNameBytes_);\n }\n\n /// @inheritdoc IFluidOracle\n function getExchangeRate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_);\n}\n"
|
||
},
|
||
"contracts/oracle/fluidOracleL2.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidOracle } from \"./interfaces/iFluidOracle.sol\";\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { IChainlinkAggregatorV3 } from \"./interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { Error as OracleError } from \"./error.sol\";\n\n/// @title FluidOracleL2\n/// @notice Base contract that any Fluid Oracle L2 must implement\nabstract contract FluidOracleL2 is IFluidOracle, OracleError {\n /// @dev Chainlink L2 Sequencer Uptime feed to detect sequencer outages\n IChainlinkAggregatorV3 internal _SEQUENCER_ORACLE;\n /// @dev max time period until oracle assumes normal behavior after a sequencer outage.\n uint256 internal constant _SEQUENCER_MAX_GRACE_PERIOD = 45 minutes;\n\n /// @notice sets the L2 sequencer uptime Chainlink feed\n constructor(address sequencerUptimeFeed_) {\n _SEQUENCER_ORACLE = IChainlinkAggregatorV3(sequencerUptimeFeed_);\n }\n\n /// @notice returns all sequencer uptime feed related data\n function sequencerL2Data()\n public\n view\n returns (\n address sequencerUptimeFeed_,\n uint256 maxGracePeriod_,\n bool isSequencerUp_,\n uint256 lastUptimeStartedAt_,\n uint256 gracePeriod_,\n bool gracePeriodPassed_,\n uint256 lastOutageStartedAt_,\n bool isSequencerUpAndValid_\n )\n {\n uint80 uptimeStartRoundId_;\n (isSequencerUp_, uptimeStartRoundId_, lastUptimeStartedAt_) = _sequencerUpStatus();\n\n if (isSequencerUp_) {\n (gracePeriod_, gracePeriodPassed_, lastOutageStartedAt_) = _gracePeriod(\n uptimeStartRoundId_,\n lastUptimeStartedAt_\n );\n } else {\n gracePeriod_ = _SEQUENCER_MAX_GRACE_PERIOD;\n (uint80 roundId_, , , , ) = _SEQUENCER_ORACLE.latestRoundData();\n lastOutageStartedAt_ = _lastSequencerOutageStart(roundId_ + 1);\n }\n\n return (\n address(_SEQUENCER_ORACLE),\n _SEQUENCER_MAX_GRACE_PERIOD,\n isSequencerUp_,\n lastUptimeStartedAt_,\n gracePeriod_,\n gracePeriodPassed_,\n lastOutageStartedAt_,\n isSequencerUp_ && gracePeriodPassed_\n );\n }\n\n /// @dev ensures that the sequencer is up and grace period has passed\n function _ensureSequencerUpAndValid() internal view {\n (bool isSequencerUp_, uint80 uptimeStartRoundId_, uint256 uptimeStartedAt_) = _sequencerUpStatus();\n\n if (!isSequencerUp_) {\n revert FluidOracleError(ErrorTypes.FluidOracleL2__SequencerOutage);\n }\n\n (, bool gracePeriodPassed_, ) = _gracePeriod(uptimeStartRoundId_, uptimeStartedAt_);\n if (!gracePeriodPassed_) {\n revert FluidOracleError(ErrorTypes.FluidOracleL2__SequencerOutage);\n }\n }\n\n /// @inheritdoc IFluidOracle\n function getExchangeRate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_);\n\n /// @dev finds last round before `uptimeStartRoundId_` where sequencer status was down, incl. handling cases of\n /// consecutive rounds where status was down.\n function _lastSequencerOutageStart(uint80 uptimeStartRoundId_) private view returns (uint256 outageStartedAt_) {\n uint80 roundId_ = uptimeStartRoundId_;\n int256 answer_;\n uint256 startedAt_;\n do {\n (roundId_, answer_, startedAt_, , ) = _SEQUENCER_ORACLE.getRoundData(roundId_ - 1);\n if (answer_ != 0) {\n // sequencer was down at this round, update outage started at data\n outageStartedAt_ = startedAt_;\n } // else: while loop is going to break\n } while (answer_ != 0 && startedAt_ > 0);\n }\n\n /// @dev finds last round where sequencer status was up, incl. handling cases of consecutive rounds where status was up.\n function _sequencerUpStatus()\n private\n view\n returns (bool isSequencerUp_, uint80 uptimeStartRoundId_, uint256 uptimeStartedAt_)\n {\n (uint80 roundId_, int256 answer_, uint256 startedAt_, , ) = _SEQUENCER_ORACLE.latestRoundData();\n if (answer_ != 0) {\n // sequencer is down currently.\n return (false, 0, 0);\n }\n\n isSequencerUp_ = true;\n\n // cover case where there were other consecutive uptime report rounds in between\n uptimeStartRoundId_ = roundId_;\n uptimeStartedAt_ = startedAt_;\n if (uptimeStartedAt_ > 0) {\n do {\n (roundId_, answer_, startedAt_, , ) = _SEQUENCER_ORACLE.getRoundData(roundId_ - 1);\n if (answer_ == 0) {\n // sequencer was up at this round, consecutive uptime so update uptime start data\n uptimeStartRoundId_ = roundId_;\n uptimeStartedAt_ = startedAt_;\n } // else: while loop is going to break\n } while (answer_ == 0 && startedAt_ > 0);\n } // else if startedAt == 0, then it is the first ever round.\n }\n\n /// @dev returns the `gracePeriod_` duration and if the grace period has `passed_` based on\n /// current uptime round data vs the last sequencer outage duration.\n function _gracePeriod(\n uint80 uptimeStartRoundId_,\n uint256 uptimeStartedAt_\n ) private view returns (uint256 gracePeriod_, bool passed_, uint256 outageStartedAt_) {\n uint256 uptimeDuration_ = block.timestamp - uptimeStartedAt_;\n if (uptimeStartedAt_ == 0 || uptimeDuration_ > _SEQUENCER_MAX_GRACE_PERIOD) {\n return (_SEQUENCER_MAX_GRACE_PERIOD, true, 0);\n }\n\n outageStartedAt_ = _lastSequencerOutageStart(uptimeStartRoundId_);\n\n // grace period is outage duration, capped at _SEQUENCER_MAX_GRACE_PERIOD\n gracePeriod_ = uptimeStartedAt_ - outageStartedAt_; // outage duration\n if (gracePeriod_ > _SEQUENCER_MAX_GRACE_PERIOD) {\n gracePeriod_ = _SEQUENCER_MAX_GRACE_PERIOD;\n }\n\n return (gracePeriod_, uptimeDuration_ > gracePeriod_, outageStartedAt_);\n }\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/pendleOracleImpl.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IPendleMarketV3 } from \"../interfaces/external/IPendleMarketV3.sol\";\nimport { IPendlePYLpOracle } from \"../interfaces/external/IPendlePYLpOracle.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title Pendle Oracle implementation\n/// @notice This contract is used to get the exchange rate for a Pendle market (PT-Asset).\nabstract contract PendleOracleImpl is OracleError {\n /// @dev Pendle pyYtLpOracle address, see Pendle docs for deployment address.\n IPendlePYLpOracle internal immutable _PENDLE_ORACLE;\n\n /// @dev Pendle market address for which this Oracle is intended for.\n IPendleMarketV3 internal immutable _PENDLE_MARKET;\n\n /// @dev timestamp when PT reaches maturity. read and stored from Immutable at the `_PENDLE_MARKET` contract.\n uint256 internal immutable _EXPIRY;\n\n /// @dev TWAP duration for the pendle AMM oracle rate fetch.\n /// The recommended duration is 15 mins (900 secs) or 30 mins (1800 secs), but it can vary depending on the market.\n /// See https://docs.pendle.finance/Developers/Oracles/HowToIntegratePtAndLpOracle#second-choose-a-market--duration\n uint32 internal immutable _TWAP_DURATION;\n\n /// @dev maximum expected borrow rate for the borrow asset at the Fluid vault. Affects the increasing price for\n /// operate(), creating an effective CF at the vault that increases as we get closer to maturity.\n uint256 internal immutable _MAX_EXPECTED_BORROW_RATE;\n\n /// @dev minimum ever expected yield rate at Pendle for the PT asset. If TWAP price is outside of this range,\n /// the oracle will revert, stopping any new borrows during a time of unexpected state of the Pendle market.\n uint256 internal immutable _MIN_YIELD_RATE;\n\n /// @dev maximum ever expected yield rate at Pendle for the PT asset. If TWAP price is outside of this range,\n /// the oracle will revert, stopping any new borrows during a time of unexpected state of the Pendle market.\n uint256 internal immutable _MAX_YIELD_RATE;\n\n /// @dev decimals of the debt token for correct scaling out the output rate\n uint8 internal immutable _DEBT_TOKEN_DECIMALS;\n\n uint8 internal constant _PENDLE_DECIMALS = 18;\n\n constructor(\n IPendlePYLpOracle pendleOracle_,\n IPendleMarketV3 pendleMarket_,\n uint32 twapDuration_,\n uint256 maxExpectedBorrowRate_,\n uint256 minYieldRate_,\n uint256 maxYieldRate_,\n uint8 debtTokenDecimals_\n ) {\n if (\n address(pendleOracle_) == address(0) ||\n address(pendleMarket_) == address(0) ||\n twapDuration_ == 0 ||\n // human input sanity checks:\n // max expected yield / borrow rate values should be >0 and below 300% (<100% for min yield rate)\n maxExpectedBorrowRate_ == 0 ||\n maxExpectedBorrowRate_ > 300 * 1e2 ||\n minYieldRate_ == 0 ||\n minYieldRate_ > 100 * 1e2 ||\n maxYieldRate_ == 0 ||\n maxYieldRate_ > 300 * 1e2 ||\n minYieldRate_ > maxYieldRate_ ||\n debtTokenDecimals_ < 6\n ) {\n revert FluidOracleError(ErrorTypes.PendleOracle__InvalidParams);\n }\n\n {\n (bool increaseCardinalityRequired_, , bool oldestObservationSatisfied_) = pendleOracle_.getOracleState(\n address(pendleMarket_),\n twapDuration_\n );\n if (increaseCardinalityRequired_ || !oldestObservationSatisfied_) {\n // ensure pendle market Oracle is ready and initialized see\n // https://docs.pendle.finance/Developers/Oracles/HowToIntegratePtAndLpOracle\n revert FluidOracleError(ErrorTypes.PendleOracle__MarketNotInitialized);\n }\n }\n\n if (\n pendleMarket_.decimals() != _PENDLE_DECIMALS ||\n // getPtToAssetRate should be returned in 1e18, otherwise this oracle will be faulty.\n // if the returned price is < 1e15, decimals are off and the issue should be investigated.\n pendleOracle_.getPtToAssetRate(address(pendleMarket_), twapDuration_) < 1e15\n ) {\n // pendle market should have 18 decimals, other markets currently don't exist. If different, might have to adjust\n // code so better to sanity check & revert.\n revert FluidOracleError(ErrorTypes.PendleOracle__MarketInvalidDecimals);\n }\n\n _PENDLE_ORACLE = pendleOracle_;\n _PENDLE_MARKET = pendleMarket_;\n _EXPIRY = pendleMarket_.expiry();\n _TWAP_DURATION = twapDuration_;\n _MAX_EXPECTED_BORROW_RATE = maxExpectedBorrowRate_;\n _MIN_YIELD_RATE = minYieldRate_;\n _MAX_YIELD_RATE = maxYieldRate_;\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 PT-sUSDe and USDC (this Oracle returning amount of USDC for 1e18 PT-sUSDe).\n _DEBT_TOKEN_DECIMALS = debtTokenDecimals_;\n }\n\n /// @dev returns the pendle oracle exchange rate for operate() scaled by `OracleUtils.RATE_OUTPUT_DECIMALS`.\n /// checks that the AMM TWAP rate at Pendle is within the allowed yield ranges, and returns\n /// the `rate_` based on maturity and a maximum expected borrow rate at Fluid, resulting into an automatically\n /// with block.timestamp adjusting effective CF at the vault, increasing as we get closer to maturity.\n function _getPendleExchangeRateOperate() internal view returns (uint256 rate_) {\n uint256 timeToMaturity_;\n unchecked {\n timeToMaturity_ = _EXPIRY > block.timestamp ? _EXPIRY - block.timestamp : 0;\n }\n if (timeToMaturity_ == 0) {\n // at maturity, 1PT is always 1 underlying.\n return (10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + _DEBT_TOKEN_DECIMALS)) / (10 ** _PENDLE_DECIMALS);\n }\n\n // get TWAP price from Pendle AMM.\n // Note getPtToAssetRate() gives the price of PT to the underlying asset at maturity.\n // For PT-sUSDe this would be USDe, not sUSDe (sUSDe -> USDe fetched from contract pricing)!\n rate_ =\n _PENDLE_ORACLE.getPtToAssetRate(address(_PENDLE_MARKET), _TWAP_DURATION) *\n (10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - _PENDLE_DECIMALS)); // * 1e9 to scale to 1e27\n // check if within allowed ranges\n // 1PT can never be more than 1:1 to asset,\n if (rate_ > 10 ** OracleUtils.RATE_OUTPUT_DECIMALS) {\n // this should never happen, even at maturity the max price would be 1:1\n revert FluidOracleError(ErrorTypes.PendleOracle__InvalidPrice);\n }\n\n // price should be within _MIN_YIELD_RATE & _MAX_YIELD_RATE for time to maturity.\n // Note max yield results in a smaller price for the PT asset and vice versa for min.\n uint256 minExpectedPrice_ = _priceAtRateToMaturity(_MAX_YIELD_RATE, timeToMaturity_);\n uint256 maxExpectedPrice_ = _priceAtRateToMaturity(_MIN_YIELD_RATE, timeToMaturity_);\n\n if (rate_ < minExpectedPrice_ || rate_ > maxExpectedPrice_) {\n revert FluidOracleError(ErrorTypes.PendleOracle__InvalidPrice);\n }\n\n // for operate return peg price based on maturity and a maximum expected borrow rate at Fluid. This results in\n // an effective decreased CF at the vault depending on time to maturity.\n // example for a Fluid vault PT-SUSDE / USDC, as we assume price at maturity:\n // 1 PT-SUSDE = 1 USDE and 1 USDE = 1 USDC. where this oracle is responsible for the 1 PT-SUSDE = 1 USDE part.\n // with a CF of 85% at the vault, that means a user can borrow 0.85 USDC for 1 PT-SUSDE.\n // Our goal is to guarantee there is no possibility for bad debt at maturity. So effective CF should be time dependent to maturity\n // based on a max expected borrow rate.\n // e.g. at 50% max borrow rate and maturity in 100 days, _priceAtRateToMaturity would return:\n // x = 1e20 * 1e27 / (1e20 + (5000 * 1e16 * 100 days / 365 days)) = 879518072289156626\n\n // now this oracle reports instead of 1 PT-SUSDE = 1 USDE, 1 PT-SUSDE = 0.879518072289156626 USDE.\n // which leads to a user can borrow 0.879518072289156626 USDE * 0.85 CF = 0.747590361445783132 USDC for 1 PT-SUSDE.\n\n // this automatically adjusts the closer we get to maturity. E.g. at 1 day to maturity:\n // x = 1e20 * 1e27 / (1e20 + (5000 * 1e16 * 1 days / 365 days)) = 998632010943912448\n // -> user can borrow 0.998632010943912448 USDE * 0.85 CF = 0.848837209302325581 USDC for 1 PT-SUSDE.\n\n rate_ = _priceAtRateToMaturity(_MAX_EXPECTED_BORROW_RATE, timeToMaturity_);\n // scale result:\n // e.g. for PT-SUSDE -> USDC: rate * 10^6 / 10^18 = result will be in 1e15\n // e.g. for PT-SUSDE -> DAI: rate * 10^18 / 10^18 = result will be in 1e27\n rate_ = (rate_ * (10 ** _DEBT_TOKEN_DECIMALS)) / (10 ** _PENDLE_DECIMALS);\n }\n\n /// @dev returns the pendle oracle exchange rate for liquidate(): 1PT = 1 underlying (e.g. 1PT-sUSDE = 1 USDE).\n /// scaled by `OracleUtils.RATE_OUTPUT_DECIMALS`.\n function _getPendleExchangeRateLiquidate() internal view returns (uint256 rate_) {\n // for liquidate, peg at maturity is assumed: 1PT = 1 underlying (e.g. 1PT-sUSDE = 1 USDE).\n // this avoids unnecessary liquidation cascades. Any bad debt would be temporary until maturity only.\n // see scaling info in `_getPendleExchangeRateOperate()`\n return (10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + _DEBT_TOKEN_DECIMALS)) / (10 ** _PENDLE_DECIMALS);\n }\n\n /// @notice returns all Pendle oracle related data as utility for easy off-chain use / block explorer in a single view method\n function pendleOracleData()\n public\n view\n returns (\n IPendlePYLpOracle pendleOracle_,\n IPendleMarketV3 pendleMarket_,\n uint256 expiry_,\n uint32 twapDuration_,\n uint256 maxExpectedBorrowRate_,\n uint256 minYieldRate_,\n uint256 maxYieldRate_,\n uint8 debtTokenDecimals_,\n uint256 exchangeRateOperate_,\n uint256 exchangeRateLiquidate_,\n uint256 ptToAssetRateTWAP_\n )\n {\n return (\n _PENDLE_ORACLE,\n _PENDLE_MARKET,\n _EXPIRY,\n _TWAP_DURATION,\n _MAX_EXPECTED_BORROW_RATE,\n _MIN_YIELD_RATE,\n _MAX_YIELD_RATE,\n _DEBT_TOKEN_DECIMALS,\n _getPendleExchangeRateOperate(),\n _getPendleExchangeRateLiquidate(),\n _PENDLE_ORACLE.getPtToAssetRate(address(_PENDLE_MARKET), _TWAP_DURATION)\n );\n }\n\n /// @dev returns the `price_` in 1e27, given a `yearlyRatePercent_` yield in percent (1e2 = 1%) and a `timeToMaturity_`.\n function _priceAtRateToMaturity(\n uint256 yearlyRatePercent_,\n uint256 timeToMaturity_\n ) internal pure returns (uint256 price_) {\n // formula: x = 100% / (100% + (yearlyRatePercent * timeToMaturity / 1year)\n // with scaling (100% = 1e20, result scaled to 1e27):\n // x = 1e20 * 1e27 / (1e20 +(yearlyRatePercent * 1e16 * timeToMaturity / 1year))\n // e.g. when 100 days to maturity and yield rate is 4%\n // x = 1e20 * 1e27 / (1e20 + (400 * 1e16 * 100 days /365 days))\n return\n (1e20 * (10 ** OracleUtils.RATE_OUTPUT_DECIMALS)) /\n (1e20 + ((yearlyRatePercent_ * 1e16 * timeToMaturity_) / 365 days));\n }\n}\n"
|
||
},
|
||
"contracts/oracle/implementations/redstoneOracleImpl.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { RedstoneStructs } from \"./structs.sol\";\n\n/// @title Redstone Oracle implementation\n/// @notice This contract is used to get the exchange rate from a Redstone Oracle\nabstract contract RedstoneOracleImpl is OracleError, RedstoneStructs {\n /// @notice Redstone price oracle to check for the exchange rate\n IRedstoneOracle internal immutable _REDSTONE_ORACLE;\n /// @notice Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _REDSTONE_INVERT_RATE;\n\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _REDSTONE_PRICE_SCALER_MULTIPLIER;\n /// @notice constant value for inverting price to reduce gas usage\n uint256 internal immutable _REDSTONE_INVERT_PRICE_DIVIDEND;\n\n address internal immutable _REDSTONE_ORACLE_NOT_SET_ADDRESS = 0x000000000000000000000000000000000000dEaD;\n\n /// @notice constructor sets the Redstone oracle data\n constructor(RedstoneOracleData memory oracleData_) {\n if (address(oracleData_.oracle) == address(0) || oracleData_.token0Decimals == 0) {\n revert FluidOracleError(ErrorTypes.RedstoneOracle__InvalidParams);\n }\n\n _REDSTONE_ORACLE = oracleData_.oracle;\n _REDSTONE_INVERT_RATE = oracleData_.invertRate;\n\n // for explanation on how to get to scaler multiplier and dividend see `chainlinkOracleImpl.sol`.\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _REDSTONE_PRICE_SCALER_MULTIPLIER = address(oracleData_.oracle) == _REDSTONE_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - oracleData_.token0Decimals);\n _REDSTONE_INVERT_PRICE_DIVIDEND = address(oracleData_.oracle) == _REDSTONE_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + oracleData_.token0Decimals);\n }\n\n /// @dev Get the exchange rate from Redstone oracle\n /// @param rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getRedstoneExchangeRate() internal view returns (uint256 rate_) {\n try _REDSTONE_ORACLE.getExchangeRate() returns (uint256 exchangeRate_) {\n if (_REDSTONE_INVERT_RATE) {\n // invert the price\n return _REDSTONE_INVERT_PRICE_DIVIDEND / exchangeRate_;\n } else {\n return exchangeRate_ * _REDSTONE_PRICE_SCALER_MULTIPLIER;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Redstone oracle related data as utility for easy off-chain use / block explorer in a single view method\n function redstoneOracleData()\n public\n view\n returns (uint256 redstoneExchangeRate_, IRedstoneOracle redstoneOracle_, bool redstoneInvertRate_)\n {\n return (\n address(_REDSTONE_ORACLE) == _REDSTONE_ORACLE_NOT_SET_ADDRESS ? 0 : _getRedstoneExchangeRate(),\n _REDSTONE_ORACLE,\n _REDSTONE_INVERT_RATE\n );\n }\n}\n"
|
||
},
|
||
"contracts/oracle/implementations/redstoneOracleImpl2.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { RedstoneStructs } from \"./structs.sol\";\n\n// @dev Exact same contract as RedstoneOracleImpl, just with all vars, immutables etc. renamed with a \"2\" to avoid\n// conflicts when RedstoneOracleImpl would have to be inherited twice.\n\n/// @title Redstone Oracle implementation\n/// @notice This contract is used to get the exchange rate from a Redstone Oracle\nabstract contract RedstoneOracleImpl2 is OracleError, RedstoneStructs {\n /// @notice Redstone price oracle to check for the exchange rate\n IRedstoneOracle internal immutable _REDSTONE2_ORACLE;\n /// @notice Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _REDSTONE2_INVERT_RATE;\n\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _REDSTONE2_PRICE_SCALER_MULTIPLIER;\n /// @notice constant value for inverting price to reduce gas usage\n uint256 internal immutable _REDSTONE2_INVERT_PRICE_DIVIDEND;\n\n address internal immutable _REDSTONE2_ORACLE_NOT_SET_ADDRESS = 0x000000000000000000000000000000000000dEaD;\n\n /// @notice constructor sets the Redstone oracle data\n constructor(RedstoneOracleData memory oracleData_) {\n if (address(oracleData_.oracle) == address(0) || oracleData_.token0Decimals == 0) {\n revert FluidOracleError(ErrorTypes.RedstoneOracle__InvalidParams);\n }\n\n _REDSTONE2_ORACLE = oracleData_.oracle;\n _REDSTONE2_INVERT_RATE = oracleData_.invertRate;\n\n // for explanation on how to get to scaler multiplier and dividend see `chainlinkOracleImpl.sol`.\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _REDSTONE2_PRICE_SCALER_MULTIPLIER = address(oracleData_.oracle) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - oracleData_.token0Decimals);\n _REDSTONE2_INVERT_PRICE_DIVIDEND = address(oracleData_.oracle) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + oracleData_.token0Decimals);\n }\n\n /// @dev Get the exchange rate from Redstone oracle\n /// @param rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getRedstoneExchangeRate2() internal view returns (uint256 rate_) {\n try _REDSTONE2_ORACLE.getExchangeRate() returns (uint256 exchangeRate_) {\n if (_REDSTONE2_INVERT_RATE) {\n // invert the price\n return _REDSTONE2_INVERT_PRICE_DIVIDEND / exchangeRate_;\n } else {\n return exchangeRate_ * _REDSTONE2_PRICE_SCALER_MULTIPLIER;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Redstone oracle related data as utility for easy off-chain use / block explorer in a single view method\n function redstoneOracleData2()\n public\n view\n returns (uint256 redstoneExchangeRate_, IRedstoneOracle redstoneOracle_, bool redstoneInvertRate_)\n {\n return (\n address(_REDSTONE2_ORACLE) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS ? 0 : _getRedstoneExchangeRate2(),\n _REDSTONE2_ORACLE,\n _REDSTONE2_INVERT_RATE\n );\n }\n}\n"
|
||
},
|
||
"contracts/oracle/implementations/structs.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IChainlinkAggregatorV3 } from \"../interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\n\nabstract contract ChainlinkStructs {\n struct ChainlinkFeedData {\n /// @param feed address of Chainlink feed.\n IChainlinkAggregatorV3 feed;\n /// @param invertRate true if rate read from price feed must be inverted.\n bool invertRate;\n /// @param token0Decimals decimals of asset 0. E.g. for a USDC/ETH feed, USDC is token0 and has 6 decimals.\n /// (token1Decimals are available directly via Chainlink `FEED.decimals()`)\n uint256 token0Decimals;\n }\n\n struct ChainlinkConstructorParams {\n /// @param param hops count of hops, used for sanity checks. Must be 1, 2 or 3.\n uint8 hops;\n /// @param feed1 Chainlink feed 1 data. Required.\n ChainlinkFeedData feed1;\n /// @param feed2 Chainlink feed 2 data. Required if hops > 1.\n ChainlinkFeedData feed2;\n /// @param feed3 Chainlink feed 3 data. Required if hops > 2.\n ChainlinkFeedData feed3;\n }\n}\n\nabstract contract RedstoneStructs {\n struct RedstoneOracleData {\n /// @param oracle address of Redstone oracle.\n IRedstoneOracle oracle;\n /// @param invertRate true if rate read from price feed must be inverted.\n bool invertRate;\n /// @param token0Decimals decimals of asset 0. E.g. for a USDC/ETH feed, USDC is token0 and has 6 decimals.\n /// (token1Decimals are available directly via Redstone `Oracle.decimals()`)\n uint256 token0Decimals;\n }\n}\n"
|
||
},
|
||
"contracts/oracle/implementations/sUSDeOracleImpl.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title sUSDe Oracle Implementation\n/// @notice This contract is used to get the exchange rate between sUSDe and USDe, adjusted for token decimals\n/// of a debt token (e.g. USDC / USDT)\nabstract contract SUSDeOracleImpl is OracleError {\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _SUSDE_PRICE_SCALER_MULTIPLIER;\n\n /// @notice SUSDE contract, e.g. on mainnet 0x9d39a5de30e57443bff2a8307a4256c8797a3497\n IERC4626 internal immutable _SUSDE;\n\n uint8 internal immutable _DEBT_TOKEN_DECIMALS;\n\n /// @notice constructor sets the sUSDe `sUSDe_` token address.\n constructor(IERC4626 sUSDe_, uint8 debtTokenDecimals_) {\n if (address(sUSDe_) == address(0) || debtTokenDecimals_ < 6) {\n revert FluidOracleError(ErrorTypes.SUSDeOracle__InvalidParams);\n }\n\n _SUSDE = sUSDe_;\n\n // debt token decimals is used to make sure the returned exchange rate is scaled correctly e.g.\n // for an exchange rate between sUSDe and USDC (this Oracle returning amount of USDC for 1e18 sUSDe).\n _DEBT_TOKEN_DECIMALS = debtTokenDecimals_;\n\n _SUSDE_PRICE_SCALER_MULTIPLIER = 10 ** (debtTokenDecimals_ - 6);\n // e.g. when:\n // - debtTokenDecimals_ = 6 -> scaler multiplier is 1\n // - debtTokenDecimals_ = 7 -> scaler multiplier is 10\n // - debtTokenDecimals_ = 18 -> scaler multiplier is 1e12\n // -> gets 1e15 returned exchange rate to 1e27\n }\n\n /// @notice Get the exchange rate from sUSDe contract (amount of USDe for 1 sUSDe)\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getSUSDeExchangeRate() internal view returns (uint256 rate_) {\n return _SUSDE.convertToAssets(1e15) * _SUSDE_PRICE_SCALER_MULTIPLIER;\n }\n\n /// @notice returns all sUSDe oracle related data as utility for easy off-chain use / block explorer in a single view method\n function sUSDeOracleData()\n public\n view\n returns (uint256 sUSDeExchangeRate_, IERC4626 sUSDe_, uint256 debtTokenDecimals_)\n {\n return (_getSUSDeExchangeRate(), _SUSDE, _DEBT_TOKEN_DECIMALS);\n }\n}\n"
|
||
},
|
||
"contracts/oracle/implementations/uniV3OracleImpl.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { FullMath } from \"../libraries/FullMath.sol\";\nimport { TickMath } from \"../libraries/TickMath.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { IUniswapV3Pool } from \"../interfaces/external/IUniswapV3Pool.sol\";\nimport { Error as OracleError } from \"../error.sol\";\n\n/// @title Uniswap V3 Oracle implementation\n/// @notice This contract is used to get the exchange rate from from a Uniswap V3 Pool,\n/// including logic to check against TWAP max deltas.\n/// @dev Uses 5 secondsAgos[] values and 3 TWAP maxDeltas:\n/// e.g. [240, 60, 15, 1, 0] -> [price240to60, price60to15, price 15to1, currentPrice]\n/// delta checks: price240to60 vs currentPrice, price60to15 vs currentPrice and 15to1 vs currentPrice.\nabstract contract UniV3OracleImpl is OracleError {\n /// @dev Uniswap V3 Pool to check for the exchange rate\n IUniswapV3Pool internal immutable _POOL;\n\n /// @dev Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _UNIV3_INVERT_RATE;\n\n /// @dev Uniswap oracle delta for TWAP1 in 1e2 percent. If uniswap price TWAP1 is out of this delta,\n /// current price fetching reverts. E.g. for delta of TWAP 240 -> 60 vs current price\n uint256 internal immutable _UNI_TWAP1_MAX_DELTA_PERCENT;\n /// @dev Uniswap oracle delta for TWAP2 in 1e2 percent. If uniswap price TWAP2 is out of this delta,\n /// current price fetching reverts. E.g. for delta of TWAP 60 -> 15 vs current price\n uint256 internal immutable _UNI_TWAP2_MAX_DELTA_PERCENT;\n /// @dev Uniswap oracle delta for TWAP3 in 1e2 percent. If uniswap price TWAP3 is out of this delta,\n /// current price fetching reverts. E.g. for delta of TWAP 15 -> 1 vs current price\n uint256 internal immutable _UNI_TWAP3_MAX_DELTA_PERCENT;\n\n /// @dev Uniswap oracle seconds ago for twap, 1. value, e.g. 240\n uint256 internal immutable _UNI_SECONDS_AGO_1;\n /// @dev Uniswap oracle seconds ago for twap, 2. value, e.g. 60\n uint256 internal immutable _UNI_SECONDS_AGO_2;\n /// @dev Uniswap oracle seconds ago for twap, 3. value, e.g. 15\n uint256 internal immutable _UNI_SECONDS_AGO_3;\n /// @dev Uniswap oracle seconds ago for twap, 4. value, e.g. 1\n uint256 internal immutable _UNI_SECONDS_AGO_4;\n /// @dev Uniswap oracle seconds ago for twap, 5. value, e.g. 0\n uint256 internal immutable _UNI_SECONDS_AGO_5;\n\n /// @dev Uniswap TWAP1 interval duration.\n int256 internal immutable _UNI_TWAP1_INTERVAL;\n /// @dev Uniswap TWAP2 interval duration.\n int256 internal immutable _UNI_TWAP2_INTERVAL;\n /// @dev Uniswap TWAP3 interval duration.\n int256 internal immutable _UNI_TWAP3_INTERVAL;\n /// @dev Uniswap TWAP4 interval duration.\n int256 internal immutable _UNI_TWAP4_INTERVAL;\n\n /// @dev stored array lengths to optimize gas\n uint256 internal constant _SECONDS_AGOS_LENGTH = 5;\n uint256 internal constant _TWAP_DELTAS_LENGTH = 3;\n\n /// @dev constant value for price scaling to reduce gas usage\n uint256 internal immutable _UNIV3_PRICE_SCALER_MULTIPLIER;\n /// @dev constant value for inverting price to reduce gas usage\n uint256 internal immutable _UNIV3_INVERT_PRICE_DIVIDEND;\n\n struct UniV3ConstructorParams {\n /// @param pool Uniswap V3 Pool to check for the exchange rate\n IUniswapV3Pool pool;\n /// @param invertRate Flag to invert the Uniswap price or not\n bool invertRate;\n /// @param tWAPMaxDeltaPercents Uniswap oracle delta for TWAP1-2-3 in 1e2 percent\n uint256[_TWAP_DELTAS_LENGTH] tWAPMaxDeltaPercents;\n /// @param secondsAgos Uniswap oracle seconds ago for the 3 TWAP values, from oldest to newest, e.g. [240, 60, 15, 1, 0]\n uint32[_SECONDS_AGOS_LENGTH] secondsAgos;\n }\n\n /// @notice constructor sets the Uniswap V3 `pool_` to check for the exchange rate and the `invertRate_` flag.\n /// E.g. `invertRate_` should be true if for the WETH/USDC pool it's expected that the oracle returns USDC per 1 WETH\n constructor(UniV3ConstructorParams memory params_) {\n if (address(params_.pool) == address(0)) {\n revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidParams);\n }\n // sanity check that seconds agos values are ordered ascending, e.g. [240, 60, 15, 1, 0]\n if (\n params_.secondsAgos[0] <= params_.secondsAgos[1] ||\n params_.secondsAgos[1] <= params_.secondsAgos[2] ||\n params_.secondsAgos[2] <= params_.secondsAgos[3] ||\n params_.secondsAgos[3] <= params_.secondsAgos[4]\n ) {\n revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidSecondsAgos);\n }\n // sanity check that deltas are less than 100% and decreasing (as timespan is closer to current price):\n // 1. delta must < 100%\n // all following deltas must be <= than the previous one\n if (\n params_.tWAPMaxDeltaPercents[0] >= OracleUtils.HUNDRED_PERCENT_DELTA_SCALER ||\n params_.tWAPMaxDeltaPercents[1] > params_.tWAPMaxDeltaPercents[0] ||\n params_.tWAPMaxDeltaPercents[2] > params_.tWAPMaxDeltaPercents[1]\n ) {\n revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidDeltas);\n }\n\n _UNI_SECONDS_AGO_1 = uint256(params_.secondsAgos[0]);\n _UNI_SECONDS_AGO_2 = uint256(params_.secondsAgos[1]);\n _UNI_SECONDS_AGO_3 = uint256(params_.secondsAgos[2]);\n _UNI_SECONDS_AGO_4 = uint256(params_.secondsAgos[3]);\n _UNI_SECONDS_AGO_5 = uint256(params_.secondsAgos[4]);\n\n _UNI_TWAP1_INTERVAL = int256(uint256(params_.secondsAgos[0] - params_.secondsAgos[1]));\n _UNI_TWAP2_INTERVAL = int256(uint256(params_.secondsAgos[1] - params_.secondsAgos[2]));\n _UNI_TWAP3_INTERVAL = int256(uint256(params_.secondsAgos[2] - params_.secondsAgos[3]));\n _UNI_TWAP4_INTERVAL = int256(uint256(params_.secondsAgos[3] - params_.secondsAgos[4]));\n\n _UNI_TWAP1_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[0]; // e.g. for TWAP 240 -> 60 vs current price\n _UNI_TWAP2_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[1]; // e.g. for TWAP 60 -> 15 vs current price\n _UNI_TWAP3_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[2]; // e.g. for TWAP 15 -> 1 vs current price\n\n _POOL = params_.pool;\n _UNIV3_INVERT_RATE = params_.invertRate;\n\n // uniswapV3 returned price is already scaled to token decimals.\n _UNIV3_PRICE_SCALER_MULTIPLIER = 10 ** OracleUtils.RATE_OUTPUT_DECIMALS;\n // uniV3 invert price dividend happens on the already scaled by 1e27 result for price in token1 per 1 token0\n _UNIV3_INVERT_PRICE_DIVIDEND = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS * 2);\n }\n\n /// @dev Get the last exchange rate from the pool's last observed value without any checks\n /// @return exchangeRateUnsafe_ The exchange rate between the underlying asset and the peg asset in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getUniV3ExchangeRateUnsafe() internal view returns (uint256 exchangeRateUnsafe_) {\n (uint160 sqrtPriceX96_, , , , , , ) = _POOL.slot0();\n\n exchangeRateUnsafe_ = _UNIV3_INVERT_RATE\n ? _invertUniV3Price(_getPriceFromSqrtPriceX96(sqrtPriceX96_))\n : _getPriceFromSqrtPriceX96(sqrtPriceX96_);\n }\n\n /// @dev Get the last exchange rate from the pool's last observed value, checked against TWAP deviations.\n /// @return exchangeRate_ The exchange rate between the underlying asset and the peg asset in `OracleUtils.RATE_OUTPUT_DECIMALS`\n /// If 0 then the fetching the price failed or a delta was invalid.\n function _getUniV3ExchangeRate() internal view returns (uint256 exchangeRate_) {\n // build calldata bytes in a gas-optimized way without having to build an array / using abi.encode.\n // gas efficient work around for Solidity not supporting immutable non-value types.\n bytes memory data_ = abi.encodePacked(\n hex\"883bdbfd\", // pack function selector\n hex\"0000000000000000000000000000000000000000000000000000000000000020\", // pack start offset of dynamic array\n _SECONDS_AGOS_LENGTH, // pack length of dynamic array\n // pack seconds agos values:\n _UNI_SECONDS_AGO_1,\n _UNI_SECONDS_AGO_2,\n _UNI_SECONDS_AGO_3,\n _UNI_SECONDS_AGO_4,\n _UNI_SECONDS_AGO_5\n );\n\n // get the tickCumulatives from Pool.observe()\n (bool success_, bytes memory result_) = address(_POOL).staticcall(data_);\n\n if (!success_) {\n return 0;\n }\n int56[] memory tickCumulatives_ = abi.decode(result_, (int56[]));\n\n unchecked {\n int24 exchangeRateTick_;\n {\n int56 tickCumulativesDelta_ = (tickCumulatives_[_TWAP_DELTAS_LENGTH + 1] -\n tickCumulatives_[_TWAP_DELTAS_LENGTH]);\n // _UNI_TWAP4_INTERVAL can not be 0 because of constructor sanity checks\n exchangeRateTick_ = int24(tickCumulativesDelta_ / _UNI_TWAP4_INTERVAL);\n // Always round to negative infinity, see UniV3 OracleLibrary\n // https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/libraries/OracleLibrary.sol#L36\n if (tickCumulativesDelta_ < 0 && (tickCumulativesDelta_ % _UNI_TWAP4_INTERVAL != 0)) {\n exchangeRateTick_--;\n }\n }\n\n // Check the latest Uniswap price is within the acceptable delta from each TWAP range\n // TWAP 1 check\n if (\n _isInvalidTWAPDelta(\n int256(exchangeRateTick_),\n tickCumulatives_[1] - tickCumulatives_[0],\n _UNI_TWAP1_INTERVAL,\n int256(_UNI_TWAP1_MAX_DELTA_PERCENT)\n )\n ) {\n return 0;\n }\n\n // TWAP 2 check\n if (\n _isInvalidTWAPDelta(\n int256(exchangeRateTick_),\n tickCumulatives_[2] - tickCumulatives_[1],\n _UNI_TWAP2_INTERVAL,\n int256(_UNI_TWAP2_MAX_DELTA_PERCENT)\n )\n ) {\n return 0;\n }\n\n // TWAP 3 check\n if (\n _isInvalidTWAPDelta(\n int256(exchangeRateTick_),\n tickCumulatives_[3] - tickCumulatives_[2],\n _UNI_TWAP3_INTERVAL,\n int256(_UNI_TWAP3_MAX_DELTA_PERCENT)\n )\n ) {\n return 0;\n }\n\n // get the current uniswap price, which is the last tick cumulatives interval, usually [..., 1, 0]\n exchangeRate_ = _getPriceFromSqrtPriceX96(TickMath.getSqrtRatioAtTick(exchangeRateTick_));\n if (_UNIV3_INVERT_RATE) {\n exchangeRate_ = _invertUniV3Price(exchangeRate_);\n }\n }\n }\n\n /// @dev verifies that `exchangeRate_` is within `maxDelta_` for derived price from `tickCumulativesDelta_` and `interval_`.\n /// returns true if delta is invalid\n function _isInvalidTWAPDelta(\n int256 exchangeRateTick_,\n int256 tickCumulativesDelta_,\n int256 interval_, // can not be 0 because of constructor sanity checks\n int256 maxDelta_\n ) internal pure returns (bool) {\n unchecked {\n int256 arithmeticMeanTick_ = int256(tickCumulativesDelta_ / interval_);\n // Always round to negative infinity, see UniV3 OracleLibrary\n // https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/libraries/OracleLibrary.sol#L36\n if (tickCumulativesDelta_ < 0 && (tickCumulativesDelta_ % interval_ != 0)) {\n arithmeticMeanTick_--;\n }\n\n // Check that the uniswapPrice is within DELTA of the Uniswap TWAP (via tick)\n // each univ3 tick is 0.01% increase or decrease in price. `maxDelta_` has near to same precision.\n // Note: near to the same because each Uniswap tick is 0.01% away so price of ticks are if current one is 100 then next will be:\n // 100 + 100 * 0.01% = 100.01\n // 100.01 + 100.01 * 0.01% = 100.020001\n if (\n exchangeRateTick_ > (arithmeticMeanTick_ + maxDelta_) ||\n exchangeRateTick_ < (arithmeticMeanTick_ - maxDelta_)\n ) {\n // Uniswap last price is NOT within the delta\n return true;\n }\n }\n return false;\n }\n\n /// @notice returns all UniV3 oracle related data as utility for easy off-chain use / block explorer in a single view method\n function uniV3OracleData()\n public\n view\n returns (\n IUniswapV3Pool uniV3Pool_,\n bool uniV3InvertRate_,\n uint32[] memory uniV3secondsAgos_,\n uint256[] memory uniV3TwapDeltas_,\n uint256 uniV3exchangeRateUnsafe_,\n uint256 uniV3exchangeRate_\n )\n {\n // Get the latest TWAP prices from the Uniswap Oracle for second intervals\n uniV3secondsAgos_ = new uint32[](_SECONDS_AGOS_LENGTH);\n uniV3secondsAgos_[0] = uint32(_UNI_SECONDS_AGO_1);\n uniV3secondsAgos_[1] = uint32(_UNI_SECONDS_AGO_2);\n uniV3secondsAgos_[2] = uint32(_UNI_SECONDS_AGO_3);\n uniV3secondsAgos_[3] = uint32(_UNI_SECONDS_AGO_4);\n uniV3secondsAgos_[4] = uint32(_UNI_SECONDS_AGO_5);\n\n // Check the latest Uniswap price is within the acceptable delta from each TWAP range\n uniV3TwapDeltas_ = new uint256[](_TWAP_DELTAS_LENGTH);\n uniV3TwapDeltas_[0] = _UNI_TWAP1_MAX_DELTA_PERCENT;\n uniV3TwapDeltas_[1] = _UNI_TWAP2_MAX_DELTA_PERCENT;\n uniV3TwapDeltas_[2] = _UNI_TWAP3_MAX_DELTA_PERCENT;\n\n return (\n _POOL,\n _UNIV3_INVERT_RATE,\n uniV3secondsAgos_,\n uniV3TwapDeltas_,\n _getUniV3ExchangeRateUnsafe(),\n _getUniV3ExchangeRate()\n );\n }\n\n /// @dev Get the price from the sqrt price in `OracleUtils.RATE_OUTPUT_DECIMALS`\n /// (see https://blog.uniswap.org/uniswap-v3-math-primer)\n /// @param sqrtPriceX96_ The sqrt price to convert\n function _getPriceFromSqrtPriceX96(uint160 sqrtPriceX96_) private view returns (uint256 priceX96_) {\n return\n FullMath.mulDiv(\n uint256(sqrtPriceX96_) * uint256(sqrtPriceX96_),\n _UNIV3_PRICE_SCALER_MULTIPLIER,\n 1 << 192 // 2^96 * 2\n );\n }\n\n /// @dev Invert the price\n /// @param price_ The price to invert\n /// @return invertedPrice_ The inverted price in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _invertUniV3Price(uint256 price_) private view returns (uint256 invertedPrice_) {\n return _UNIV3_INVERT_PRICE_DIVIDEND / price_;\n }\n}\n"
|
||
},
|
||
"contracts/oracle/implementations/weETHOracleImpl.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title weETH Oracle Implementation\n/// @notice This contract is used to get the exchange rate between weETH and eETH\nabstract contract WeETHOracleImpl is OracleError {\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _WEETH_PRICE_SCALER_MULTIPLIER;\n\n /// @notice WEETH contract, e.g. on mainnet 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee\n IWeETH internal immutable _WEETH;\n\n /// @notice constructor sets the weETH (Etherfi's wrapped eETH) `weETH_` token address.\n constructor(IWeETH weETH_) {\n if (address(weETH_) == address(0)) {\n revert FluidOracleError(ErrorTypes.WeETHOracle__InvalidParams);\n }\n\n _WEETH = weETH_;\n\n _WEETH_PRICE_SCALER_MULTIPLIER = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - 18); // e.g. 1e9\n }\n\n /// @notice Get the exchange rate from weETH contract\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getWeETHExchangeRate() internal view returns (uint256 rate_) {\n return _WEETH.getEETHByWeETH(1e18) * _WEETH_PRICE_SCALER_MULTIPLIER;\n }\n\n /// @notice returns all weETH oracle related data as utility for easy off-chain use / block explorer in a single view method\n function weETHOracleData() public view returns (uint256 weETHExchangeRate_, IWeETH weETH_) {\n return (_getWeETHExchangeRate(), _WEETH);\n }\n}\n"
|
||
},
|
||
"contracts/oracle/implementations/wstETHOracleImpl.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title wstETH Oracle Implementation\n/// @notice This contract is used to get the exchange rate between wstETH and stETH\nabstract contract WstETHOracleImpl is OracleError {\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _WSTETH_PRICE_SCALER_MULTIPLIER;\n\n /// @notice WSTETH contract, e.g. on mainnet 0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0\n IWstETH internal immutable _WSTETH;\n\n /// @notice constructor sets the wstETH `wstETH_` token address.\n constructor(IWstETH wstETH_) {\n if (address(wstETH_) == address(0)) {\n revert FluidOracleError(ErrorTypes.WstETHOracle__InvalidParams);\n }\n\n _WSTETH = wstETH_;\n\n _WSTETH_PRICE_SCALER_MULTIPLIER = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - 18); // e.g. 1e9\n }\n\n /// @notice Get the exchange rate from wstETH contract\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getWstETHExchangeRate() internal view returns (uint256 rate_) {\n return _WSTETH.stEthPerToken() * _WSTETH_PRICE_SCALER_MULTIPLIER;\n }\n\n /// @notice returns all wWtETH oracle related data as utility for easy off-chain use / block explorer in a single view method\n function wstETHOracleData() public view returns (uint256 wstETHExchangeRate_, IWstETH wstETH_) {\n return (_getWstETHExchangeRate(), _WSTETH);\n }\n}\n"
|
||
},
|
||
"contracts/oracle/interfaces/external/IChainlinkAggregatorV3.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\n/// from https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\n/// Copyright (c) 2018 SmartContract ChainLink, Ltd.\n\ninterface IChainlinkAggregatorV3 {\n /// @notice represents the number of decimals the aggregator responses represent.\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n function getRoundData(\n uint80 _roundId\n )\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n\n function latestRoundData()\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n}\n"
|
||
},
|
||
"contracts/oracle/interfaces/external/IPendleMarketV3.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IPendleMarketV3 {\n function decimals() external view returns (uint8);\n\n function expiry() external view returns (uint256);\n\n function increaseObservationsCardinalityNext(uint16 cardinalityNext) external;\n}\n"
|
||
},
|
||
"contracts/oracle/interfaces/external/IPendlePYLpOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IPendlePYLpOracle {\n function getPtToAssetRate(address market, uint32 duration) external view returns (uint256);\n\n function getYtToAssetRate(address market, uint32 duration) external view returns (uint256);\n\n function getLpToAssetRate(address market, uint32 duration) external view returns (uint256);\n\n function getPtToSyRate(address market, uint32 duration) external view returns (uint256);\n\n function getYtToSyRate(address market, uint32 duration) external view returns (uint256);\n\n function getLpToSyRate(address market, uint32 duration) external view returns (uint256);\n\n function getOracleState(\n address market,\n uint32 duration\n )\n external\n view\n returns (bool increaseCardinalityRequired, uint16 cardinalityRequired, bool oldestObservationSatisfied);\n}\n"
|
||
},
|
||
"contracts/oracle/interfaces/external/IRedstoneOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IRedstoneOracle {\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset\n // @dev custom Redstone adapter for Instadapp implementation\n function getExchangeRate() external view returns (uint256 exchangeRate_);\n\n /**\n * @notice Returns the number of decimals for the price feed\n * @dev By default, RedStone uses 8 decimals for data feeds\n * @return decimals The number of decimals in the price feed values\n */\n // see https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol#L51C12-L51C20\n function decimals() external view returns (uint8);\n}\n"
|
||
},
|
||
"contracts/oracle/interfaces/external/IUniswapV3Pool.sol": {
|
||
"content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.21;\n\n/// from https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces.\n/// Copyright (c) 2022 Uniswap Labs\n\n/// @title Pool state that never changes\n/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values\ninterface IUniswapV3PoolImmutables {\n /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface\n /// @return The contract address\n function factory() external view returns (address);\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6\n /// @return The fee\n function fee() external view returns (uint24);\n\n /// @notice The pool tick spacing\n /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive\n /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...\n /// This value is an int24 to avoid casting even though it is always positive.\n /// @return The tick spacing\n function tickSpacing() external view returns (int24);\n\n /// @notice The maximum amount of position liquidity that can use any tick in the range\n /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and\n /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool\n /// @return The max amount of liquidity per tick\n function maxLiquidityPerTick() external view returns (uint128);\n}\n\n/// @title Pool state that is not stored\n/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the\n/// blockchain. The functions here may have variable gas costs.\ninterface IUniswapV3PoolDerivedState {\n /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp\n /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing\n /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,\n /// you must call it with secondsAgos = [3600, 0].\n /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in\n /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.\n /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned\n /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp\n /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block\n /// timestamp\n function observe(\n uint32[] calldata secondsAgos\n ) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);\n\n /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range\n /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.\n /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first\n /// snapshot is taken and the second snapshot is taken.\n /// @param tickLower The lower tick of the range\n /// @param tickUpper The upper tick of the range\n /// @return tickCumulativeInside The snapshot of the tick accumulator for the range\n /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range\n /// @return secondsInside The snapshot of seconds per liquidity for the range\n function snapshotCumulativesInside(\n int24 tickLower,\n int24 tickUpper\n ) external view returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);\n}\n\n/// @title Pool state that can change\n/// @notice These methods compose the pool's state, and can change with any frequency including multiple times\n/// per transaction\ninterface IUniswapV3PoolState {\n /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas\n /// when accessed externally.\n /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value\n /// tick The current tick of the pool, i.e. according to the last tick transition that was run.\n /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick\n /// boundary.\n /// observationIndex The index of the last oracle observation that was written,\n /// observationCardinality The current maximum number of observations stored in the pool,\n /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.\n /// feeProtocol The protocol fee for both tokens of the pool.\n /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0\n /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.\n /// unlocked Whether the pool is currently locked to reentrancy\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n uint8 feeProtocol,\n bool unlocked\n );\n\n /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool\n /// @dev This value can overflow the uint256\n function feeGrowthGlobal0X128() external view returns (uint256);\n\n /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool\n /// @dev This value can overflow the uint256\n function feeGrowthGlobal1X128() external view returns (uint256);\n\n /// @notice The amounts of token0 and token1 that are owed to the protocol\n /// @dev Protocol fees will never exceed uint128 max in either token\n function protocolFees() external view returns (uint128 token0, uint128 token1);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(\n int24 tick\n )\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n\n /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information\n function tickBitmap(int16 wordPosition) external view returns (uint256);\n\n /// @notice Returns the information about a position by the position's key\n /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper\n /// @return _liquidity The amount of liquidity in the position,\n /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,\n /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,\n /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,\n /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke\n function positions(\n bytes32 key\n )\n external\n view\n returns (\n uint128 _liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n /// @notice Returns data about a specific observation index\n /// @param index The element of the observations array to fetch\n /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time\n /// ago, rather than at a specific index in the array.\n /// @return blockTimestamp The timestamp of the observation,\n /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,\n /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,\n /// Returns initialized whether the observation has been initialized and the values are safe to use\n function observations(\n uint256 index\n )\n external\n view\n returns (\n uint32 blockTimestamp,\n int56 tickCumulative,\n uint160 secondsPerLiquidityCumulativeX128,\n bool initialized\n );\n}\n\n/// @title The interface for a Uniswap V3 Pool\n/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface IUniswapV3Pool is IUniswapV3PoolImmutables, IUniswapV3PoolState, IUniswapV3PoolDerivedState {\n\n}\n"
|
||
},
|
||
"contracts/oracle/interfaces/external/IWeETH.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IWeETH {\n /**\n * @notice Get amount of eETH for {_weETHAmount} weETH\n * @return Amount of eETH for {_weETHAmount} weETH\n */\n function getEETHByWeETH(uint256 _weETHAmount) external view returns (uint256);\n\n /**\n * @notice Get amount of weETH for {_eETHAmount} eETH\n * @return Amount of weETH for {_eETHAmount} eETH\n */\n function getWeETHByeETH(uint256 _eETHAmount) external view returns (uint256);\n}\n"
|
||
},
|
||
"contracts/oracle/interfaces/external/IWstETH.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IWstETH {\n /**\n * @notice Get amount of stETH for 1 wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for 1 stETH\n * @return Amount of wstETH for 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n}\n"
|
||
},
|
||
"contracts/oracle/interfaces/iFluidOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidOracle {\n /// @dev Deprecated. Use `getExchangeRateOperate()` and `getExchangeRateLiquidate()` instead. Only implemented for\n /// backwards compatibility.\n function getExchangeRate() external view returns (uint256 exchangeRate_);\n\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for operates\n function getExchangeRateOperate() external view returns (uint256 exchangeRate_);\n\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for liquidations\n function getExchangeRateLiquidate() external view returns (uint256 exchangeRate_);\n\n /// @notice helper string to easily identify the oracle. E.g. token symbols\n function infoName() external view returns (string memory);\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 infoName_ Oracle identify helper name.\n /// @param chainlinkParams_ ChainlinkOracle constructor params struct.\n /// @param uniV3Params_ UniV3Oracle constructor params struct.\n constructor(\n string memory infoName_,\n ChainlinkConstructorParams memory chainlinkParams_,\n UniV3ConstructorParams memory uniV3Params_\n ) ChainlinkOracleImpl(chainlinkParams_) UniV3OracleImpl(uniV3Params_) FluidOracle(infoName_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view virtual override returns (uint256 exchangeRate_) {\n exchangeRate_ = _getChainlinkExchangeRate();\n if (exchangeRate_ == 0) {\n // if Chainlink fails, use UniV3 rate (TWAP checked)\n exchangeRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n revert FluidOracleError(ErrorTypes.CLFallbackUniV3Oracle__ExchangeRateZero);\n }\n }\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() public view virtual override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view virtual override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/fallbackCLRSOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title Chainlink / Redstone Oracle (with fallback)\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a Chainlink price feed or a Redstone Oracle with one of them being used as main source and\n/// the other one acting as a fallback if the main source fails for any reason. Reverts if fetched rate is 0.\ncontract FallbackCLRSOracle is FluidOracle, FallbackOracleImpl {\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param infoName_ Oracle identify helper name.\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 string memory infoName_,\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n ) FallbackOracleImpl(mainSource_, chainlinkParams_, redstoneOracle_) FluidOracle(infoName_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view virtual override returns (uint256 exchangeRate_) {\n (exchangeRate_, ) = _getRateWithFallback();\n\n if (exchangeRate_ == 0) {\n revert FluidOracleError(ErrorTypes.FallbackCLRSOracle__ExchangeRateZero);\n }\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() public view virtual override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view virtual override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE_MAIN_SOURCE;\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/pendleOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { IPendleMarketV3 } from \"../interfaces/external/IPendleMarketV3.sol\";\nimport { IPendlePYLpOracle } from \"../interfaces/external/IPendlePYLpOracle.sol\";\nimport { PendleOracleImpl } from \"../implementations/pendleOracleImpl.sol\";\n\n/// @title PendleOracle\n/// @notice Gets the exchange rate between Pendle and the underlying asset for the Pendle Market.\ncontract PendleOracle is FluidOracle, PendleOracleImpl {\n constructor(\n string memory infoName_,\n IPendlePYLpOracle pendleOracle_,\n IPendleMarketV3 pendleMarket_,\n uint32 twapDuration_,\n uint256 maxExpectedBorrowRate_,\n uint256 minYieldRate_,\n uint256 maxYieldRate_,\n uint8 debtTokenDecimals_\n )\n PendleOracleImpl(\n pendleOracle_,\n pendleMarket_,\n twapDuration_,\n maxExpectedBorrowRate_,\n minYieldRate_,\n maxYieldRate_,\n debtTokenDecimals_\n )\n FluidOracle(infoName_)\n {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getPendleExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return _getPendleExchangeRateLiquidate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return _getPendleExchangeRateOperate();\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(\n string memory infoName_,\n IERC4626 sUSDe_,\n uint8 debtTokenDecimals_\n ) SUSDeOracleImpl(sUSDe_, debtTokenDecimals_) FluidOracle(infoName_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/uniV3CheckCLRSOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { UniV3OracleImpl } from \"../implementations/uniV3OracleImpl.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title UniswapV3 checked against Chainlink / Redstone Oracle. Either one reported as exchange rate.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a UniV3 pool (compared against 3 TWAPs) and (optionally) comparing it against a Chainlink\n/// or Redstone price (one of Chainlink or Redstone being the main source and the other one the fallback source).\n/// Alternatively it can also use Chainlink / Redstone as main price and use UniV3 as check price.\n/// @dev The process for getting the aggregate oracle price is:\n/// 1. Fetch the UniV3 TWAPS, the latest interval is used as the current price\n/// 2. Verify this price is within an acceptable DELTA from the Uniswap TWAPS e.g.:\n/// a. 240 to 60s\n/// b. 60 to 15s\n/// c. 15 to 1s (last block)\n/// d. 1 to 0s (current)\n/// 3. (unless UniV3 only mode): Verify this price is within an acceptable DELTA from the Chainlink / Redstone Oracle\n/// 4. If it passes all checks, return the price. Otherwise use fallbacks, usually to Chainlink. In extreme edge-cases revert.\n/// @dev For UniV3 with check mode, if fetching the check price fails, the UniV3 rate is used directly.\ncontract UniV3CheckCLRSOracle is FluidOracle, UniV3OracleImpl, FallbackOracleImpl {\n /// @dev Rate check oracle delta percent in 1e2 percent. If current uniswap price is out of this delta,\n /// current price fetching reverts.\n uint256 internal immutable _RATE_CHECK_MAX_DELTA_PERCENT;\n\n /// @dev which oracle to use as final rate source:\n /// - 1 = UniV3 ONLY (no check),\n /// - 2 = UniV3 with Chainlink / Redstone check\n /// - 3 = Chainlink / Redstone with UniV3 used as check.\n uint8 internal immutable _RATE_SOURCE;\n\n struct UniV3CheckCLRSConstructorParams {\n /// @param uniV3Params UniV3Oracle constructor params struct.\n UniV3ConstructorParams uniV3Params;\n /// @param chainlinkParams ChainlinkOracle constructor params struct for UniV3CheckCLRSOracle.\n ChainlinkConstructorParams chainlinkParams;\n /// @param redstoneOracle Redstone Oracle data for UniV3CheckCLRSOracle. (address can be set to zero address if using Chainlink only)\n RedstoneOracleData redstoneOracle;\n /// @param rateSource which oracle to use as final rate source for UniV3CheckCLRSOracle:\n /// - 1 = UniV3 ONLY (no check),\n /// - 2 = UniV3 with Chainlink / Redstone check\n /// - 3 = Chainlink / Redstone with UniV3 used as check.\n uint8 rateSource;\n /// @param fallbackMainSource which oracle to use as CL/RS main source for UniV3CheckCLRSOracle: see FallbackOracleImpl constructor `mainSource_`\n uint8 fallbackMainSource;\n /// @param rateCheckMaxDeltaPercent Rate check oracle delta in 1e2 percent for UniV3CheckCLRSOracle\n uint256 rateCheckMaxDeltaPercent;\n }\n\n constructor(\n string memory infoName_,\n UniV3CheckCLRSConstructorParams memory params_\n )\n UniV3OracleImpl(params_.uniV3Params)\n FallbackOracleImpl(params_.fallbackMainSource, params_.chainlinkParams, params_.redstoneOracle)\n FluidOracle(infoName_)\n {\n if (\n params_.rateSource < 1 ||\n params_.rateSource > 3 ||\n params_.rateCheckMaxDeltaPercent > OracleUtils.HUNDRED_PERCENT_DELTA_SCALER ||\n // Chainlink only Oracle with UniV3 check. Delta would be ignored so revert this type of Oracle setup.\n (params_.fallbackMainSource == 1 && params_.rateSource == 3)\n ) {\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidParams);\n }\n\n _RATE_CHECK_MAX_DELTA_PERCENT = params_.rateCheckMaxDeltaPercent;\n _RATE_SOURCE = params_.rateSource;\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @notice returns all oracle related data as utility for easy off-chain / block explorer use in a single view method\n function uniV3CheckOracleData()\n public\n view\n returns (uint256 rateCheckMaxDelta_, uint256 rateSource_, uint256 fallbackMainSource_)\n {\n return (_RATE_CHECK_MAX_DELTA_PERCENT, _RATE_SOURCE, _FALLBACK_ORACLE_MAIN_SOURCE);\n }\n\n function _getExchangeRate() internal view returns (uint256 exchangeRate_) {\n if (_RATE_SOURCE == 1) {\n // uniswap is the only main source without check:\n // 1. get uniV3 rate.\n // 2. If that fails (outside delta range) -> revert (no other Oracle configured).\n exchangeRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n // fetching UniV3 failed or invalid delta -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n return exchangeRate_;\n }\n\n uint256 checkRate_;\n bool fallback_;\n if (_RATE_SOURCE == 2) {\n // uniswap is main source, with Chainlink / Redstone as check\n // 1. get uniV3 rate\n\n // case uniV3 rate fails (outside delta range):\n // 2. get Chainlink rate. -> if successful, use Chainlink as result\n // 3. if Chainlink fails too, get Redstone -> if successful, use Redstone as result\n // 4. if Redstone fails too, revert\n\n // case if uniV3 rate is ok\n // 2. get Chainlink or Redstone rate for check (one is configured as main check source, other one is fallback source)\n // -> if both fail to fetch, use uniV3 rate directly.\n // 3. check the delta for uniV3 rate against the check soure rate. -> if ok, return uniV3 rate\n // 4. if delta check fails, check delta against the fallback check source. -> if ok, return uniV3 rate\n // 5. if delta check fails for both sources, return Chainlink price\n\n exchangeRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n // uniV3 failed or invalid delta -> use (Chainlink with Redstone as fallback)\n exchangeRate_ = _getChainlinkOrRedstoneAsFallback();\n if (exchangeRate_ == 0) {\n // Chainlink / Redstone failed too -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n return exchangeRate_;\n }\n\n (checkRate_, fallback_) = _getRateWithFallback();\n if (checkRate_ == 0) {\n // check price source failed to fetch -> directly use uniV3 TWAP checked price\n // Note uniV3 price fetching was successful, would have been caught otherwise above.\n return exchangeRate_;\n }\n } else {\n // Chainlink / Redstone is main source, with uniV3 as check.\n // 1. get Chainlink / Redstone rate (one is configured as main source, other one is fallback source)\n\n // case when both Chainlink & Redstone fail:\n // 2. get uniV3 rate. if successful, use uniV3 rate. otherwise, revert (all oracles failed).\n\n // case when Chainlink / Redstone fetch is successful:\n // 2. get uniV3 rate for check.\n // 3. if uniV3 rate fails to fetch (outside delta), use Chainlink / Redstone directly (skip check).\n // 4. if uniV3 rate is ok, check the delta for Chainlink / Redstone rate against uniV3 rate.\n // -> if ok, return Chainlink / Redstone (main) rate\n // 5. if delta check fails, check delta against the fallback main source.\n // -> if ok, return fallback main rate\n // 6. if delta check fails for both sources, return Chainlink price.\n\n (exchangeRate_, fallback_) = _getRateWithFallback();\n checkRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n if (checkRate_ == 0) {\n // all oracles failed, revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // Both Chainlink & Redstone failed -> directly use uniV3 TWAP checked price\n // Note uniV3 price fetching was successful, would have been caught otherwise above.\n return checkRate_;\n }\n\n if (checkRate_ == 0) {\n // uniV3 failed -> skip check against Uniswap price.\n\n return exchangeRate_;\n }\n }\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n if (fallback_) {\n // fallback already used, no other rate available to check.\n\n // if price is chainlink price -> return it.\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 3) {\n // redstone with Chainlink as fallback\n return _RATE_SOURCE == 2 ? checkRate_ : exchangeRate_; // if rate source is 2, Chainlink rate is in checkRate_\n }\n\n // if price is redstone price -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidPrice);\n }\n\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 1) {\n // 1 = only chainlink and UniV3 is configured and delta check failed. no fallback available.\n if (_RATE_SOURCE == 2) {\n // case where uniV3 is main source with only Chainlink as check rate Oracle configured.\n // delta check failed -> return Chainlink price (instead of uniV3 price).\n return checkRate_;\n }\n\n // here: if (_FALLBACK_ORACLE_MAIN_SOURCE == 1 && _RATE_SOURCE == 3)\n // rate source is 3: Chainlink as main, uniV3 as delta. delta check failed.\n // this Oracle type would basically be a more expensive Chainlink-only Oracle because the delta check against UniV3 is ignored.\n // this setup is reverted in constructor, but in any case returning Chainlink price here even though this code should never be reached.\n return exchangeRate_; // exchangeRate_ here is chainlink price\n }\n\n // fallback not done yet -> check against fallback price.\n // So if originally Chainlink was fetched and delta failed, check against Redstone.\n // if originally Redstone was fetched and delta failed, check against Chainlink.\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 2) {\n // 2 = Chainlink with Redstone Fallback. delta check against Chainlink failed. try against Redstone.\n uint256 redstoneRate_ = _getRedstoneExchangeRate();\n uint256 chainlinkRate_;\n if (_RATE_SOURCE == 2) {\n // uniV3 main source. -> update checkRate_ with Redstone price\n chainlinkRate_ = checkRate_;\n checkRate_ = redstoneRate_;\n } else {\n // uniV3 is check source. -> update exchangeRate_ with Redstone price\n chainlinkRate_ = exchangeRate_;\n exchangeRate_ = redstoneRate_;\n }\n\n if (redstoneRate_ == 0) {\n // fetching Redstone failed. So delta UniV3 <> Chainlink failed, fetching Redstone as backup failed.\n // -> return chainlink price (for both cases when Chainlink is main and when UniV3 is the main source).\n return chainlinkRate_;\n }\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n // delta check against Redstone failed too. return Chainlink price\n return chainlinkRate_;\n }\n\n // delta check against Redstone passed. if uniV3 main source -> return uniV3, else return Redstone.\n // exchangeRate_ is already set correctly for this.\n } else {\n // 3 = Redstone with Chainlink Fallback. delta check against Redstone failed. try against Chainlink.\n uint256 chainlinkRate_ = _getChainlinkExchangeRate();\n if (chainlinkRate_ == 0) {\n // fetching Chainlink failed. So delta UniV3 <> Redstone failed, fetching Chainlink as backup check failed.\n // -> revert.\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidPrice);\n }\n\n if (_RATE_SOURCE == 3) {\n // uniV3 is check source. -> update exchangeRate_ with Chainlink price.\n // Optimization: in this case we can directly return chainlink price, because if delta check between\n // Chainlink (new main source) and uniV3 (check source) fails, we anyway return Chainlink price still.\n return chainlinkRate_;\n }\n\n // uniV3 main source. -> update checkRate_ with Chainlink price and compare delta again\n checkRate_ = chainlinkRate_;\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n // delta check against Chainlink failed too. case here can only be where uniV3 would have been\n // main source and Chainlink check source. -> return Chainlink as price instead of uniV3\n return checkRate_;\n }\n\n // delta check against Chainlink passed. if uniV3 main source -> return uniV3, else return Chainlink.\n // exchangeRate_ is already set correctly for this.\n }\n }\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/weETHOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\n\n/// @title WeETHOracle\n/// @notice Gets the exchange rate between weETH and eETH directly from the weETH contract.\ncontract WeETHOracle is FluidOracle, WeETHOracleImpl {\n /// @notice constructor sets the weETH `weETH_` token address.\n constructor(string memory infoName_, IWeETH weETH_) WeETHOracleImpl(weETH_) FluidOracle(infoName_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getWeETHExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/weETHUniV3CheckCLRSOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { UniV3CheckCLRSOracle } from \"./uniV3CheckCLRSOracle.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title weETHOracle combined with a uniV3CheckCLRSOracle.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// 1. weETH Oracle price for weETH -> eETH = ETH (pegged)\n/// 2. result from 1. combined with a uniV3CheckCLRSOracle to get someToken (e.g. ETH) -> someToken2.\n/// e.g. when going from weETH to USDC:\n/// 1. weETH -> eETH = ETH via weETH Oracle\n/// 2. ETH -> USDC via UniV3 ETH <> USDC pool checked against ETH -> USDC Chainlink feed.\ncontract WeETHUniV3CheckCLRSOracle is FluidOracle, WeETHOracleImpl, UniV3CheckCLRSOracle {\n /// @notice constructs a WeETHUniV3CheckCLRSOracle with all inherited contracts\n /// @param infoName_ Oracle identify helper name.\n /// @param weETH_ address of the weETH contract\n /// @param uniV3CheckCLRSParams_ UniV3CheckCLRSOracle constructor params\n constructor(\n string memory infoName_,\n IWeETH weETH_,\n UniV3CheckCLRSConstructorParams memory uniV3CheckCLRSParams_\n ) WeETHOracleImpl(weETH_) UniV3CheckCLRSOracle(infoName_, uniV3CheckCLRSParams_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0.\n // (super.getExchangeRate() returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n // Combine this rate with the weETH -> eETH = ETH rate.\n exchangeRate_ =\n (super.getExchangeRateOperate() * _getWeETHExchangeRate()) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0.\n // (super.getExchangeRate() returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n // Combine this rate with the weETH -> eETH = ETH rate.\n exchangeRate_ =\n (super.getExchangeRateLiquidate() * _getWeETHExchangeRate()) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view override(FluidOracle, UniV3CheckCLRSOracle) returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/weETHwstETHOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title Oracle for weETH (Etherfi's wrapped eETH) to wstETH. wstETH is the debt token here (get amount of wstETH for 1 weETH)\ncontract WeETHWstETHOracle is FluidOracle, WstETHOracleImpl, WeETHOracleImpl {\n /// @param infoName_ Oracle identify helper name.\n /// @param wstETH address of the wstETH contract\n /// @param weETH address of the weETH contract\n constructor(\n string memory infoName_,\n IWstETH wstETH,\n IWeETH weETH\n ) WstETHOracleImpl(wstETH) WeETHOracleImpl(weETH) FluidOracle(infoName_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n // weEth -> wstETH\n exchangeRate_ =\n (_WEETH.getEETHByWeETH(1e18) * (10 ** OracleUtils.RATE_OUTPUT_DECIMALS)) /\n _WSTETH.stEthPerToken();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/wstETHCLRS2UniV3CheckCLRSOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { UniV3CheckCLRSOracle } from \"./uniV3CheckCLRSOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { FallbackOracleImpl2 } from \"../implementations/fallbackOracleImpl2.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n// @dev uses FallbackOracleImpl2 to avoid conflicts with already used ChainlinkOracleImpl, RedstoneOracleImpl and\n// FallbackOracleImpl in UniV3CheckCLRSOracle.\n\n/// @title wstETHCLRSOracle combined with a uniV3CheckCLRSOracle.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// 1. wstETH Oracle price in combination with rate from Chainlink price feeds (or Redstone as fallback).\n/// combining those two into one rate resulting in wstETH <> someToken\n/// 2. result from 1. combined with a uniV3CheckCLRSOracle to get from someToken <> someToken2\n/// e.g. when going from wstETH to USDC:\n/// 1. wstETH -> stETH wstETH Oracle, stETH -> ETH Chainlink feed.\n/// 2. ETH -> USDC via UniV3 ETH <> USDC pool checked against ETH -> USDC Chainlink feed.\ncontract WstETHCLRS2UniV3CheckCLRSOracle is FluidOracle, WstETHOracleImpl, FallbackOracleImpl2, UniV3CheckCLRSOracle {\n struct WstETHCLRS2ConstructorParams {\n /// @param wstETH address of the wstETH contract\n IWstETH wstETH;\n /// @param fallbackMainSource which oracle to use as main source for wstETH <> CLRS: 1 = Chainlink, 2 = Redstone (other one is fallback).\n uint8 fallbackMainSource;\n /// @param chainlinkParams chainlink Oracle constructor params struct for wstETH <> CLRS.\n ChainlinkConstructorParams chainlinkParams;\n /// @param redstoneOracle Redstone Oracle data for wstETH <> CLRS. (address can be set to zero address if using Chainlink only)\n RedstoneOracleData redstoneOracle;\n }\n\n /// @notice constructs a WstETHCLRS2UniV3CheckCLRSOracle with all inherited contracts\n /// @param infoName_ Oracle identify helper name.\n /// @param wstETHCLRS2Params_ WstETHCLRS2ConstructorParams for wstETH <> CLRS Token2 conversion\n /// @param uniV3CheckCLRSParams_ UniV3CheckCLRSOracle constructor params\n constructor(\n string memory infoName_,\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(infoName_, uniV3CheckCLRSParams_)\n {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate for stETH <> CLRS feed. uses FallbackOracleImpl2\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // 2. combine CLRS feed price with wstETH price to have wstETH <> stETH <> SomeToken fully converted\n exchangeRate_ = (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n // 3. get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0. Combine this rate with existing.\n // (super.getExchangeRate returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n exchangeRate_ = (super.getExchangeRateOperate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate for stETH <> CLRS feed. uses FallbackOracleImpl2\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // 2. combine CLRS feed price with wstETH price to have wstETH <> stETH <> SomeToken fully converted\n exchangeRate_ = (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n // 3. get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0. Combine this rate with existing.\n // (super.getExchangeRate returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n exchangeRate_ = (super.getExchangeRateLiquidate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view override(FluidOracle, UniV3CheckCLRSOracle) returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE2_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE2_MAIN_SOURCE;\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/wstETHCLRSOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title WstETH Chainlink / Redstone Oracle (with fallback)\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// wstETH Oracle price in combination with rate from Chainlink price feeds (or Redstone as fallback),\n/// hopping the 2 rates into 1 rate.\n/// e.g. when going from wstETH to USDT:\n/// wstETH -> stETH wstETH Oracle, stETH -> ETH Chainlink feed, ETH -> USDT Chainlink feed.\ncontract WstETHCLRSOracle is FluidOracle, WstETHOracleImpl, FallbackOracleImpl {\n /// @notice sets the wstETH address, main source, Chainlink Oracle and Redstone Oracle data.\n /// @param infoName_ Oracle identify helper name.\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 string memory infoName_,\n IWstETH wstETH_,\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n )\n WstETHOracleImpl(wstETH_)\n FallbackOracleImpl(mainSource_, chainlinkParams_, redstoneOracle_)\n FluidOracle(infoName_)\n {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n (exchangeRate_, ) = _getRateWithFallback();\n\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRSOracle__ExchangeRateZero);\n }\n\n return (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE_MAIN_SOURCE;\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oracles/wstETHOracle.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\n\n/// @title WstETHOracle\n/// @notice Gets the exchange rate between wstETH and stETH directly from the wstETH contract.\ncontract WstETHOracle is FluidOracle, WstETHOracleImpl {\n /// @notice constructor sets the wstETH `wstETH_` token address.\n constructor(string memory infoName_, IWstETH wstETH_) WstETHOracleImpl(wstETH_) FluidOracle(infoName_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getWstETHExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oraclesL2/cLFallbackUniV3OracleL2.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracleL2 } from \"../fluidOracleL2.sol\";\nimport { CLFallbackUniV3Oracle } from \"../oracles/cLFallbackUniV3Oracle.sol\";\n\n/// @title Chainlink with Fallback to UniV3 Oracle for Layer 2 (with sequencer outage detection)\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 CLFallbackUniV3OracleL2 is FluidOracleL2, CLFallbackUniV3Oracle {\n /// @notice sets the Chainlink and UniV3 Oracle configs.\n /// @param infoName_ Oracle identify helper name.\n /// @param chainlinkParams_ ChainlinkOracle constructor params struct.\n /// @param uniV3Params_ UniV3Oracle constructor params struct.\n /// @param sequencerUptimeFeed_ L2 sequencer uptime Chainlink feed\n constructor(\n string memory infoName_,\n ChainlinkConstructorParams memory chainlinkParams_,\n UniV3ConstructorParams memory uniV3Params_,\n address sequencerUptimeFeed_\n ) CLFallbackUniV3Oracle(infoName_, chainlinkParams_, uniV3Params_) FluidOracleL2(sequencerUptimeFeed_) {}\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateOperate()\n public\n view\n override(CLFallbackUniV3Oracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n _ensureSequencerUpAndValid();\n return super.getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateLiquidate()\n public\n view\n override(CLFallbackUniV3Oracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n _ensureSequencerUpAndValid();\n return super.getExchangeRateLiquidate();\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRate()\n public\n view\n override(CLFallbackUniV3Oracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n return getExchangeRateOperate();\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oraclesL2/cLRS2UniV3CheckCLRSOracleL2.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracleL2 } from \"../fluidOracleL2.sol\";\nimport { UniV3CheckCLRSOracleL2 } from \"./uniV3CheckCLRSOracleL2.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { FallbackOracleImpl2 } from \"../implementations/fallbackOracleImpl2.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 CLRSOracle combined with a uniV3CheckCLRSOracle.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// 1. rate from Chainlink price feeds (or Redstone as fallback).\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 -> ETH Chainlink feed.\n/// 2. ETH -> USDC via UniV3 ETH <> USDC pool checked against ETH -> USDC Chainlink feed.\ncontract CLRS2UniV3CheckCLRSOracleL2 is FluidOracleL2, FallbackOracleImpl2, UniV3CheckCLRSOracleL2 {\n struct CLRS2ConstructorParams {\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 CLRS2UniV3CheckCLRSOracleL2 with all inherited contracts\n /// @param infoName_ Oracle identify helper name.\n /// @param cLRS2Params_ CLRS2ConstructorParams for wstETH <> CLRS Token2 conversion\n /// @param uniV3CheckCLRSParams_ UniV3CheckCLRSOracle constructor params\n /// @param sequencerUptimeFeed_ L2 sequencer uptime Chainlink feed\n constructor(\n string memory infoName_,\n CLRS2ConstructorParams memory cLRS2Params_,\n UniV3CheckCLRSConstructorParams memory uniV3CheckCLRSParams_,\n address sequencerUptimeFeed_\n )\n FallbackOracleImpl2(cLRS2Params_.fallbackMainSource, cLRS2Params_.chainlinkParams, cLRS2Params_.redstoneOracle)\n UniV3CheckCLRSOracleL2(infoName_, uniV3CheckCLRSParams_, sequencerUptimeFeed_)\n {}\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateOperate()\n public\n view\n override(FluidOracleL2, UniV3CheckCLRSOracleL2)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate for wstETH <> CLRS feed. uses FallbackOracleImpl2\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.CLRS2UniV3CheckCLRSOracleL2__ExchangeRateZero);\n }\n\n // 2. 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 UniV3CheckCLRSOracleL2 rate, no other inherited contract has this.)\n // includes _ensureSequencerUpAndValid();\n exchangeRate_ = (super.getExchangeRateOperate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateLiquidate()\n public\n view\n override(FluidOracleL2, UniV3CheckCLRSOracleL2)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate for wstETH <> CLRS feed. uses FallbackOracleImpl2\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.CLRS2UniV3CheckCLRSOracleL2__ExchangeRateZero);\n }\n\n // 2. 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 UniV3CheckCLRSOracleL2 rate, no other inherited contract has this.)\n // includes _ensureSequencerUpAndValid();\n exchangeRate_ = (super.getExchangeRateLiquidate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRate()\n public\n view\n override(FluidOracleL2, UniV3CheckCLRSOracleL2)\n returns (uint256 exchangeRate_)\n {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE2_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE2_MAIN_SOURCE;\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oraclesL2/fallbackCLRSOracleL2.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracleL2 } from \"../fluidOracleL2.sol\";\nimport { FallbackCLRSOracle } from \"../oracles/fallbackCLRSOracle.sol\";\n\n/// @title Chainlink / Redstone Oracle (with fallback) for Layer 2 (with sequencer outage detection)\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 FallbackCLRSOracleL2 is FluidOracleL2, FallbackCLRSOracle {\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param infoName_ Oracle identify helper name.\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 /// @param sequencerUptimeFeed_ L2 sequencer uptime Chainlink feed\n constructor(\n string memory infoName_,\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_,\n address sequencerUptimeFeed_\n )\n FallbackCLRSOracle(infoName_, mainSource_, chainlinkParams_, redstoneOracle_)\n FluidOracleL2(sequencerUptimeFeed_)\n {}\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateOperate()\n public\n view\n override(FallbackCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n _ensureSequencerUpAndValid();\n return super.getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateLiquidate()\n public\n view\n override(FallbackCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n _ensureSequencerUpAndValid();\n return super.getExchangeRateLiquidate();\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRate() public view override(FallbackCLRSOracle, FluidOracleL2) returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
|
||
},
|
||
"contracts/oracle/oraclesL2/uniV3CheckCLRSOracleL2.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracleL2 } from \"../fluidOracleL2.sol\";\nimport { UniV3CheckCLRSOracle } from \"../oracles/uniV3CheckCLRSOracle.sol\";\n\n/// @title UniswapV3 checked against Chainlink / Redstone Oracle for Layer 2 (with sequencer outage detection). 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 UniV3CheckCLRSOracleL2 is FluidOracleL2, UniV3CheckCLRSOracle {\n constructor(\n string memory infoName_,\n UniV3CheckCLRSConstructorParams memory params_,\n address sequencerUptimeFeed_\n ) UniV3CheckCLRSOracle(infoName_, params_) FluidOracleL2(sequencerUptimeFeed_) {}\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateOperate()\n public\n view\n virtual\n override(UniV3CheckCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n _ensureSequencerUpAndValid();\n return super.getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateLiquidate()\n public\n view\n virtual\n override(UniV3CheckCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n _ensureSequencerUpAndValid();\n return super.getExchangeRateLiquidate();\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRate()\n public\n view\n virtual\n override(UniV3CheckCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n return getExchangeRateOperate();\n }\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/liquidity/iLiquidityResolver.sol": {
|
||
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { Structs as LiquidityStructs } from \"../../../periphery/resolvers/liquidity/structs.sol\";\n\ninterface IFluidLiquidityResolver {\n /// @notice gets the `revenueAmount_` for a `token_`.\n function getRevenue(address token_) external view returns (uint256 revenueAmount_);\n\n /// @notice address of contract that gets sent the revenue. Configurable by governance\n function getRevenueCollector() external view returns (address);\n\n /// @notice Liquidity contract paused status: status = 1 -> normal. status = 2 -> paused.\n function getStatus() external view returns (uint256);\n\n /// @notice checks if `auth_` is an allowed auth on Liquidity.\n /// Auths can set most config values. E.g. contracts that automate certain flows like e.g. adding a new fToken.\n /// Governance can add/remove auths. Governance is auth by default.\n function isAuth(address auth_) external view returns (uint256);\n\n /// @notice checks if `guardian_` is an allowed Guardian on Liquidity.\n /// Guardians can pause lower class users.\n /// Governance can add/remove guardians. Governance is guardian by default.\n function isGuardian(address guardian_) external view returns (uint256);\n\n /// @notice gets user class for `user_`. Class defines which protocols can be paused by guardians.\n /// Currently there are 2 classes: 0 can be paused by guardians. 1 cannot be paused by guardians.\n /// New protocols are added as class 0 and will be upgraded to 1 over time.\n function getUserClass(address user_) external view returns (uint256);\n\n /// @notice gets exchangePricesAndConfig packed uint256 storage slot for `token_`.\n function getExchangePricesAndConfig(address token_) external view returns (uint256);\n\n /// @notice gets rateConfig packed uint256 storage slot for `token_`.\n function getRateConfig(address token_) external view returns (uint256);\n\n /// @notice gets totalAmounts packed uint256 storage slot for `token_`.\n function getTotalAmounts(address token_) external view returns (uint256);\n\n /// @notice gets configs2 packed uint256 storage slot for `token_`.\n function getConfigs2(address token_) external view returns (uint256);\n\n /// @notice gets userSupply data packed uint256 storage slot for `user_` and `token_`.\n function getUserSupply(address user_, address token_) external view returns (uint256);\n\n /// @notice gets userBorrow data packed uint256 storage slot for `user_` and `token_`.\n function getUserBorrow(address user_, address token_) external view returns (uint256);\n\n /// @notice returns all `listedTokens_` at the Liquidity contract. Once configured, a token can never be removed.\n function listedTokens() external view returns (address[] memory listedTokens_);\n\n /// @notice get the Rate config data `rateData_` for a `token_` compiled from the packed uint256 rateConfig storage slot\n function getTokenRateData(address token_) external view returns (LiquidityStructs.RateData memory rateData_);\n\n /// @notice get the Rate config datas `rateDatas_` for multiple `tokens_` compiled from the packed uint256 rateConfig storage slot\n function getTokensRateData(\n address[] calldata tokens_\n ) external view returns (LiquidityStructs.RateData[] memory rateDatas_);\n\n /// @notice returns general data for `token_` such as rates, exchange prices, utilization, fee, total amounts etc.\n function getOverallTokenData(\n address token_\n ) external view returns (LiquidityStructs.OverallTokenData memory overallTokenData_);\n\n /// @notice returns general data for multiple `tokens_` such as rates, exchange prices, utilization, fee, total amounts etc.\n function getOverallTokensData(\n address[] calldata tokens_\n ) external view returns (LiquidityStructs.OverallTokenData[] memory overallTokensData_);\n\n /// @notice returns general data for all `listedTokens()` such as rates, exchange prices, utilization, fee, total amounts etc.\n function getAllOverallTokensData()\n external\n view\n returns (LiquidityStructs.OverallTokenData[] memory overallTokensData_);\n\n /// @notice returns `user_` supply data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for `token_`\n function getUserSupplyData(\n address user_,\n address token_\n )\n external\n view\n returns (\n LiquidityStructs.UserSupplyData memory userSupplyData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n );\n\n /// @notice returns `user_` supply data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for multiple `tokens_`\n function getUserMultipleSupplyData(\n address user_,\n address[] calldata tokens_\n )\n external\n view\n returns (\n LiquidityStructs.UserSupplyData[] memory userSuppliesData_,\n LiquidityStructs.OverallTokenData[] memory overallTokensData_\n );\n\n /// @notice returns `user_` borrow data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for `token_`\n function getUserBorrowData(\n address user_,\n address token_\n )\n external\n view\n returns (\n LiquidityStructs.UserBorrowData memory userBorrowData_,\n LiquidityStructs.OverallTokenData memory overallTokenData_\n );\n\n /// @notice returns `user_` borrow data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for multiple `tokens_`\n function getUserMultipleBorrowData(\n address user_,\n address[] calldata tokens_\n )\n external\n view\n returns (\n LiquidityStructs.UserBorrowData[] memory userBorrowingsData_,\n LiquidityStructs.OverallTokenData[] memory overallTokensData_\n );\n\n /// @notice returns `user_` supply data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for multiple `supplyTokens_`\n /// and returns `user_` borrow data and general data (such as rates, exchange prices, utilization, fee, total amounts etc.) for multiple `borrowTokens_`\n function getUserMultipleBorrowSupplyData(\n address user_,\n address[] calldata supplyTokens_,\n address[] calldata borrowTokens_\n )\n external\n view\n returns (\n LiquidityStructs.UserSupplyData[] memory userSuppliesData_,\n LiquidityStructs.OverallTokenData[] memory overallSupplyTokensData_,\n LiquidityStructs.UserBorrowData[] memory userBorrowingsData_,\n LiquidityStructs.OverallTokenData[] memory overallBorrowTokensData_\n );\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/liquidity/structs.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Structs as AdminModuleStructs } from \"../../../liquidity/adminModule/structs.sol\";\n\nabstract contract Structs {\n struct RateData {\n uint256 version;\n AdminModuleStructs.RateDataV1Params rateDataV1;\n AdminModuleStructs.RateDataV2Params rateDataV2;\n }\n\n struct OverallTokenData {\n uint256 borrowRate;\n uint256 supplyRate;\n uint256 fee; // revenue fee\n uint256 lastStoredUtilization;\n uint256 storageUpdateThreshold;\n uint256 lastUpdateTimestamp;\n uint256 supplyExchangePrice;\n uint256 borrowExchangePrice;\n uint256 supplyRawInterest;\n uint256 supplyInterestFree;\n uint256 borrowRawInterest;\n uint256 borrowInterestFree;\n uint256 totalSupply;\n uint256 totalBorrow;\n uint256 revenue;\n uint256 maxUtilization; // maximum allowed utilization\n RateData rateData;\n }\n\n // amounts are always in normal (for withInterest already multiplied with exchange price)\n struct UserSupplyData {\n bool modeWithInterest; // true if mode = with interest, false = without interest\n uint256 supply; // user supply amount\n // the withdrawal limit (e.g. if 10% is the limit, and 100M is supplied, it would be 90M)\n uint256 withdrawalLimit;\n uint256 lastUpdateTimestamp;\n uint256 expandPercent; // withdrawal limit expand percent in 1e2\n uint256 expandDuration; // withdrawal limit expand duration in seconds\n uint256 baseWithdrawalLimit;\n // the current actual max withdrawable amount (e.g. if 10% is the limit, and 100M is supplied, it would be 10M)\n uint256 withdrawableUntilLimit;\n uint256 withdrawable; // actual currently withdrawable amount (supply - withdrawal Limit) & considering balance\n }\n\n // amounts are always in normal (for withInterest already multiplied with exchange price)\n struct UserBorrowData {\n bool modeWithInterest; // true if mode = with interest, false = without interest\n uint256 borrow; // user borrow amount\n uint256 borrowLimit;\n uint256 lastUpdateTimestamp;\n uint256 expandPercent;\n uint256 expandDuration;\n uint256 baseBorrowLimit;\n uint256 maxBorrowLimit;\n uint256 borrowableUntilLimit; // borrowable amount until any borrow limit (incl. max utilization limit)\n uint256 borrowable; // actual currently borrowable amount (borrow limit - already borrowed) & considering balance, max utilization\n uint256 borrowLimitUtilization; // borrow limit for `maxUtilization`\n }\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/revenue/calcsSimulatedTime.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { LibsErrorTypes as ErrorTypes } from \"../../../libraries/errorTypes.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\n\n/// @dev this is the exact same code as `LiquidityCalcs` library, just that it supports a simulated\n/// block.timestamp to expose historical calculations.\nlibrary CalcsSimulatedTime {\n error FluidCalcsSimulatedTimeError(uint256 errorId_);\n error FluidCalcsSimulatedTimeInvalidTimestamp();\n\n /// @dev constants as from Liquidity variables.sol\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n // constants used for BigMath conversion from and to storage\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;\n\n uint256 internal constant FOUR_DECIMALS = 1e4;\n uint256 internal constant TWELVE_DECIMALS = 1e12;\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X15 = 0x7fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n uint256 internal constant X33 = 0x1ffffffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC EXCHANGE PRICES /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage.\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param blockTimestamp_ simulated block.timestamp\n /// @return supplyExchangePrice_ updated supplyExchangePrice\n /// @return borrowExchangePrice_ updated borrowExchangePrice\n function calcExchangePrices(\n uint256 exchangePricesAndConfig_,\n uint256 blockTimestamp_\n ) internal pure returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n // Extracting exchange prices\n supplyExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &\n X64;\n borrowExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &\n X64;\n\n if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) {\n revert FluidCalcsSimulatedTimeError(ErrorTypes.LiquidityCalcs__ExchangePriceZero);\n }\n\n uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate\n\n // @dev HERE CUSTOM: added check for simulated timestamp\n if (\n blockTimestamp_ <\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33)\n ) {\n revert FluidCalcsSimulatedTimeInvalidTimestamp();\n }\n\n unchecked {\n // last timestamp can not be > current timestamp\n uint256 secondsSinceLastUpdate_ = blockTimestamp_ -\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33);\n\n uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) &\n X15;\n if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) {\n // if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed\n // (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // calculate new borrow exchange price.\n // formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_.\n // nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0.\n borrowExchangePrice_ +=\n (borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS);\n\n // FOR SUPPLY EXCHANGE PRICE:\n // all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest.\n // formula: previous price * supply rate * secondsSinceLastUpdate_.\n // where supply rate = (borrow rate - revenueFee%) * ratioSupplyYield. And\n // ratioSupplyYield = utilization * supplyRatio * borrowRatio\n //\n // Example:\n // supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50.\n // BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%.\n // yield is 10 (so half a year must have passed).\n // supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60.\n // borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70.\n // supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2).\n // utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield).\n // borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield):\n // x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%.\n // ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5%\n // supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5%\n // increase in supplyExchangePrice, assuming 100 as previous price.\n // 100 * 22,5% * 1/2 (half a year) = 0,1125.\n // cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20.\n\n // -------------- 1. calculate ratioSupplyYield --------------------------------\n // step1: utilization * supplyRatio (or actually part of lenders receiving yield)\n\n // temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n // if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n // else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15;\n\n if (temp_ == 1) {\n // if no raw supply: no exchange price update needed\n // (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest\n if (temp_ & 1 == 1) {\n // ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = temp_ >> 1;\n\n // Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return\n // in the if statement a little above.\n\n // based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee.\n // supplyRawInterest must become worth 30. totalSupply must become 110.\n // supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2.\n // so ratioSupplyYield must come out as 2.5 (250%).\n // supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted.\n temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision)\n // e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%).\n temp_ =\n // utilization * (100% + 100% / supplyRatio)\n (((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) *\n (1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS);\n // max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31\n } else {\n // ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n temp_ = temp_ >> 1;\n // if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0\n\n // e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%).\n temp_ =\n // 1e27 * utilization * (100% + supplyRatio) / 100%\n (1e27 *\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS + temp_)) /\n (FOUR_DECIMALS * FOUR_DECIMALS);\n // max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27\n }\n // from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31\n\n // step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield)\n if (borrowRatio_ & 1 == 1) {\n // ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n\n // Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered\n // at the beginning of the method by early return if `borrowRatio_ == 1`.\n\n // based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%.\n // so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed.\n // x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666%\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_);\n // max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield).\n } else {\n // ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n // x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%.\n borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_)));\n // borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%.\n // or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101.\n // max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield).\n }\n\n // temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%.\n // max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8\n temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54;\n\n // 2. calculate supply rate\n // temp_ => supply rate (borrow rate - revenueFee%) * ratioSupplyYield.\n // division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate)\n // Note that all calculation divisions for supplyExchangePrice are rounded down.\n // Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest\n // but more suppliers not earning interest.\n temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate\n temp_ * // ratioSupplyYield\n (FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee\n // fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17.\n\n // 3. calculate increase in supply exchange price\n supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS));\n // max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0.\n }\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC REVENUE /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage\n /// and the current balance of the Fluid liquidity contract for the token.\n /// @param totalAmounts_ total amounts packed uint256 read from storage\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))\n /// @param blockTimestamp_ simulated block.timestamp\n /// @return revenueAmount_ collectable revenue amount\n function calcRevenue(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_,\n uint256 blockTimestamp_\n ) internal pure returns (uint256 revenueAmount_) {\n // @dev no need to super-optimize this method as it is only used by admin\n\n // calculate the new exchange prices based on earned interest\n (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(\n exchangePricesAndConfig_,\n blockTimestamp_\n );\n\n // total supply = interest free + with interest converted from raw\n uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_);\n\n if (totalSupply_ > 0) {\n // available revenue: balanceOf(token) + totalBorrowings - totalLendings.\n revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_);\n // ensure there is no possible case because of rounding etc. where this would revert,\n // explicitly check if >\n revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0;\n // Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization\n // can only be revenue.\n } else {\n // if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck\n revenueAmount_ = liquidityTokenBalance_;\n }\n }\n\n /// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_`\n function getTotalSupply(\n uint256 totalAmounts_,\n uint256 supplyExchangePrice_\n ) internal pure returns (uint256 totalSupply_) {\n // totalSupply_ => supplyInterestFree\n totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;\n totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits\n totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalSupply = supplyInterestFree + supplyRawInterest normalized from raw\n totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n\n /// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_`\n function getTotalBorrow(\n uint256 totalAmounts_,\n uint256 borrowExchangePrice_\n ) internal pure returns (uint256 totalBorrow_) {\n // totalBorrow_ => borrowInterestFree\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);\n totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;\n totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw\n totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/revenue/calcsVaultSimulatedTime.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { CalcsSimulatedTime } from \"./calcsSimulatedTime.sol\";\n\n/// @dev this is the exact same code as in vault protocol codebase, just that it supports a simulated\n/// block.timestamp to expose historical calculations.\nlibrary CalcsVaultSimulatedTime {\n error FluidCalcsVaultSimulatedTimeError();\n\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n\n // @dev copied from vault protocol helper.sol and adjusted to have liquidity storage data and vault rates\n // storage data passed in instead of read\n\n /// @dev Calculates new vault exchange prices.\n /// @param vaultVariables2_ vaultVariables2 read from storage for the vault (VaultResolver.getRateRaw)\n /// @param vaultRates_ rates read from storage for the vault (VaultResolver.getVaultVariables2Raw)\n /// @param liquiditySupplyExchangePricesAndConfig_ exchange prices and config packed uint256 read from storage for supply token\n /// @param liquidityBorrowExchangePricesAndConfig_ exchange prices and config packed uint256 read from storage for borrow token\n /// @param blockTimestamp_ simulated block.timestamp\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 uint256 vaultRates_,\n uint256 liquiditySupplyExchangePricesAndConfig_,\n uint256 liquidityBorrowExchangePricesAndConfig_,\n uint256 blockTimestamp_\n )\n internal\n pure\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n )\n {\n (liqSupplyExPrice_, ) = CalcsSimulatedTime.calcExchangePrices(\n liquiditySupplyExchangePricesAndConfig_,\n blockTimestamp_\n );\n (, liqBorrowExPrice_) = CalcsSimulatedTime.calcExchangePrices(\n liquidityBorrowExchangePricesAndConfig_,\n blockTimestamp_\n );\n\n uint256 oldLiqSupplyExPrice_ = (vaultRates_ & X64);\n uint256 oldLiqBorrowExPrice_ = ((vaultRates_ >> 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 FluidCalcsVaultSimulatedTimeError();\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_ =\n ((((liqSupplyExPrice_ * 1e18) / oldLiqSupplyExPrice_) - 1e18) * (vaultVariables2_ & X16)) /\n 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_ =\n ((((liqBorrowExPrice_ * 1e18) / oldLiqBorrowExPrice_) - 1e18) * ((vaultVariables2_ >> 16) & X16)) /\n 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 // (vaultRates_ >> 128) & X64) -> last stored vault's supply token exchange price\n vaultSupplyExPrice_ = (((vaultRates_ >> 128) & X64) * (1e18 + vaultSupplyExPrice_)) / 1e18;\n // (vaultRates_ >> 192) -> last stored vault's borrow token exchange price (no need to mask with & X64 as it is anyway max 64 bits)\n vaultBorrowExPrice_ = ((vaultRates_ >> 192) * (1e18 + vaultBorrowExPrice_)) / 1e18;\n }\n }\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/revenue/main.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { LiquidityCalcs } from \"../../../libraries/liquidityCalcs.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\nimport { CalcsSimulatedTime } from \"./calcsSimulatedTime.sol\";\nimport { CalcsVaultSimulatedTime } from \"./calcsVaultSimulatedTime.sol\";\n\n/// @notice Fluid Revenue resolver\ncontract FluidRevenueResolver {\n /// @notice address of the liquidity contract\n IFluidLiquidity public immutable LIQUIDITY;\n\n /// @dev address that is mapped to the chain native token\n address internal constant _NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n uint256 internal constant X64 = 0xffffffffffffffff;\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 struct TokenRevenue {\n address token;\n uint256 revenueAmount;\n }\n\n constructor(IFluidLiquidity liquidity_) {\n LIQUIDITY = IFluidLiquidity(liquidity_);\n }\n\n /// @notice address of contract that gets sent the revenue. Configurable by governance\n function getRevenueCollector() public view returns (address) {\n return address(uint160(LIQUIDITY.readFromStorage(bytes32(0))));\n }\n\n /// @notice gets the currently uncollected `revenueAmount_` for a `token_`.\n function getRevenue(address token_) public view returns (uint256 revenueAmount_) {\n uint256 exchangePricesAndConfig_ = LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n token_\n )\n );\n if (exchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> revenue is 0\n return 0;\n }\n\n uint256 liquidityTokenBalance_ = token_ == _NATIVE_TOKEN_ADDRESS\n ? address(LIQUIDITY).balance\n : IERC20(token_).balanceOf(address(LIQUIDITY));\n\n uint256 totalAmounts_ = LIQUIDITY.readFromStorage(\n LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n token_\n )\n );\n\n return LiquidityCalcs.calcRevenue(totalAmounts_, exchangePricesAndConfig_, liquidityTokenBalance_);\n }\n\n /// @notice gets the currently uncollected revenues for all listed tokens at Liquidity\n function getRevenues() public view returns (TokenRevenue[] memory tokenRevenues_) {\n uint256 length_ = LIQUIDITY.readFromStorage(bytes32(LiquiditySlotsLink.LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT));\n\n tokenRevenues_ = new TokenRevenue[](length_);\n\n uint256 startingSlotForArrayElements_ = uint256(\n keccak256(abi.encode(LiquiditySlotsLink.LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT))\n );\n\n for (uint256 i; i < length_; i++) {\n tokenRevenues_[i].token = address(\n uint160(LIQUIDITY.readFromStorage(bytes32(startingSlotForArrayElements_ + i)))\n );\n tokenRevenues_[i].revenueAmount = getRevenue(tokenRevenues_[i].token);\n }\n }\n\n /// @notice gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from stacked\n /// uint256 storage slots and the balance of the Fluid liquidity contract for the token.\n /// @dev exposed for advanced revenue calculations\n /// @param totalAmounts_ total amounts packed uint256 read from storage\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))\n /// @return revenueAmount_ collectable revenue amount\n function calcRevenue(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_\n ) public view returns (uint256 revenueAmount_) {\n if (exchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> revenue is 0\n return 0;\n }\n return LiquidityCalcs.calcRevenue(totalAmounts_, exchangePricesAndConfig_, liquidityTokenBalance_);\n }\n\n /// @notice same as `calcRevenue`, but for a simulated `block.timestamp` set via `simulatedTimestamp_`.\n function calcRevenueSimulatedTime(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_,\n uint256 simulatedTimestamp_\n ) public pure returns (uint256 revenueAmount_) {\n if (exchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> revenue is 0\n return 0;\n }\n return\n CalcsSimulatedTime.calcRevenue(\n totalAmounts_,\n exchangePricesAndConfig_,\n liquidityTokenBalance_,\n simulatedTimestamp_\n );\n }\n\n /// @notice calculates interest (exchange prices) at Liquidity for a token given its' exchangePricesAndConfig from storage\n /// for a simulated `block.timestamp` set via `simulatedTimestamp_`.\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param simulatedTimestamp_ simulated block.timestamp\n /// @return supplyExchangePrice_ updated supplyExchangePrice\n /// @return borrowExchangePrice_ updated borrowExchangePrice\n function calcLiquidityExchangePricesSimulatedTime(\n uint256 exchangePricesAndConfig_,\n uint256 simulatedTimestamp_\n ) public pure returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n if (exchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> exchange prices are 0\n return (0, 0);\n }\n return CalcsSimulatedTime.calcExchangePrices(exchangePricesAndConfig_, simulatedTimestamp_);\n }\n\n /// @notice Calculates new vault exchange prices based on storage data for a simulated `block.timestamp` set via `simulatedTimestamp_`.\n /// @param vaultVariables2_ vaultVariables2 read from storage for the vault (VaultResolver.getRateRaw)\n /// @param vaultRates_ rates read from storage for the vault (VaultResolver.getVaultVariables2Raw)\n /// @param liquiditySupplyExchangePricesAndConfig_ exchange prices and config packed uint256 read from storage for supply token\n /// @param liquidityBorrowExchangePricesAndConfig_ exchange prices and config packed uint256 read from storage for borrow token\n /// @param simulatedTimestamp_ simulated block.timestamp\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 calcVaultExchangePricesSimulatedTime(\n uint256 vaultVariables2_,\n uint256 vaultRates_,\n uint256 liquiditySupplyExchangePricesAndConfig_,\n uint256 liquidityBorrowExchangePricesAndConfig_,\n uint256 simulatedTimestamp_\n )\n public\n pure\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n )\n {\n if (liquiditySupplyExchangePricesAndConfig_ == 0 || liquidityBorrowExchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> exchange prices are 0\n return (0, 0, 0, 0);\n }\n return\n CalcsVaultSimulatedTime.updateExchangePrices(\n vaultVariables2_,\n vaultRates_,\n liquiditySupplyExchangePricesAndConfig_,\n liquidityBorrowExchangePricesAndConfig_,\n simulatedTimestamp_\n );\n }\n\n /// @notice returns the `totalSupply_` and `totalBorrow_` at Liquidity at a certain point in time given the stacked uint256\n /// storage data for total amounts and exchange prices and config.\n function calcLiquidityTotalAmountsSimulatedTime(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 simulatedTimestamp_\n )\n public\n pure\n returns (uint256 totalSupply_, uint256 totalBorrow_, uint256 supplyExchangePrice_, uint256 borrowExchangePrice_)\n {\n if (exchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> amounts are 0\n return (0, 0, 0, 0);\n }\n\n (supplyExchangePrice_, borrowExchangePrice_) = CalcsSimulatedTime.calcExchangePrices(\n exchangePricesAndConfig_,\n simulatedTimestamp_\n );\n\n totalSupply_ = CalcsSimulatedTime.getTotalSupply(totalAmounts_, supplyExchangePrice_);\n totalBorrow_ = CalcsSimulatedTime.getTotalBorrow(totalAmounts_, borrowExchangePrice_);\n }\n\n /// @notice returns the `supply_` and `borrow_` for a user (protocol) at Liquidity at a certain point in time\n /// given the stacked uint256 storage data for total amounts and exchange prices and config.\n function calcLiquidityUserAmountsSimulatedTime(\n uint256 userSupplyData_,\n uint256 userBorrowData_,\n uint256 liquiditySupplyExchangePricesAndConfig_,\n uint256 liquidityBorrowExchangePricesAndConfig_,\n uint256 simulatedTimestamp_\n )\n public\n pure\n returns (uint256 supply_, uint256 borrow_, uint256 supplyExchangePrice_, uint256 borrowExchangePrice_)\n {\n if (liquiditySupplyExchangePricesAndConfig_ == 0 || liquidityBorrowExchangePricesAndConfig_ == 0) {\n // token is not configured at Liquidity -> amounts are 0\n return (0, 0, 0, 0);\n }\n\n (supplyExchangePrice_, ) = CalcsSimulatedTime.calcExchangePrices(\n liquiditySupplyExchangePricesAndConfig_,\n simulatedTimestamp_\n );\n\n (, borrowExchangePrice_) = CalcsSimulatedTime.calcExchangePrices(\n liquidityBorrowExchangePricesAndConfig_,\n simulatedTimestamp_\n );\n\n if (userSupplyData_ > 0) {\n // if userSupplyData_ == 0 -> user not configured yet for token at Liquidity\n\n bool modeWithInterest_ = userSupplyData_ & 1 == 1;\n supply_ = BigMathMinified.fromBigNumber(\n (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (modeWithInterest_) {\n // convert raw amounts to normal for withInterest mode\n supply_ = (supply_ * supplyExchangePrice_) / 1e12;\n }\n }\n\n if (userBorrowData_ > 0) {\n // if userBorrowData_ == 0 -> user not configured yet for token at Liquidity\n\n bool modeWithInterest_ = userBorrowData_ & 1 == 1;\n borrow_ = BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n if (modeWithInterest_) {\n // convert raw amounts to normal for withInterest mode\n borrow_ = (borrow_ * borrowExchangePrice_) / 1e12;\n }\n }\n }\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/vault/helpers.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"./variables.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { TickMath } from \"../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\n\ncontract Helpers is Variables, Structs {\n function normalSlot(uint256 slot_) public pure returns (bytes32) {\n return bytes32(slot_);\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for single mapping\n function calculateStorageSlotUintMapping(uint256 slot_, uint key_) public pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for single mapping\n function calculateStorageSlotIntMapping(uint256 slot_, int key_) public pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for double mapping\n function calculateDoubleIntUintMapping(uint256 slot_, int key1_, uint key2_) public pure returns (bytes32) {\n bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));\n return keccak256(abi.encode(key2_, intermediateSlot_));\n }\n\n function tickHelper(uint tickRaw_) public pure returns (int tick) {\n require(tickRaw_ < X20, \"invalid-number\");\n if (tickRaw_ > 0) {\n tick = tickRaw_ & 1 == 1 ? int((tickRaw_ >> 1) & X19) : -int((tickRaw_ >> 1) & X19);\n } else {\n tick = type(int).min;\n }\n }\n\n constructor(\n address factory_,\n address liquidity_,\n address liquidityResolver_\n ) Variables(factory_, liquidity_, liquidityResolver_) {}\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/vault/main.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Helpers } from \"./helpers.sol\";\nimport { TickMath } from \"../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\nimport { IFluidOracle } from \"../../../oracle/fluidOracle.sol\";\nimport { IFluidVaultT1 } from \"../../../protocols/vault/interfaces/iVaultT1.sol\";\nimport { Structs as FluidLiquidityResolverStructs } from \"../liquidity/structs.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { LiquidityCalcs } from \"../../../libraries/liquidityCalcs.sol\";\n\ninterface TokenInterface {\n function balanceOf(address) external view returns (uint);\n}\n\n/// @notice Fluid Vault protocol resolver\n/// Implements various view-only methods to give easy access to Vault protocol data.\ncontract FluidVaultResolver is Helpers {\n constructor(\n address factory_,\n address liquidity_,\n address liquidityResolver_\n ) Helpers(factory_, liquidity_, liquidityResolver_) {}\n\n function getVaultAddress(uint256 vaultId_) public view returns (address vault_) {\n // @dev based on https://ethereum.stackexchange.com/a/61413\n bytes memory data;\n if (vaultId_ == 0x00) {\n // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment\n return address(0);\n } else if (vaultId_ <= 0x7f) {\n data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), address(FACTORY), uint8(vaultId_));\n } else if (vaultId_ <= 0xff) {\n data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), address(FACTORY), bytes1(0x81), uint8(vaultId_));\n } else if (vaultId_ <= 0xffff) {\n data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), address(FACTORY), bytes1(0x82), uint16(vaultId_));\n } else if (vaultId_ <= 0xffffff) {\n data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), address(FACTORY), bytes1(0x83), uint24(vaultId_));\n } else {\n data = abi.encodePacked(bytes1(0xda), bytes1(0x94), address(FACTORY), bytes1(0x84), uint32(vaultId_));\n }\n\n return address(uint160(uint256(keccak256(data))));\n }\n\n function getVaultId(address vault_) public view returns (uint id_) {\n id_ = IFluidVaultT1(vault_).VAULT_ID();\n }\n\n function getTokenConfig(uint nftId_) public view returns (uint) {\n return FACTORY.readFromStorage(calculateStorageSlotUintMapping(3, nftId_));\n }\n\n function getVaultVariablesRaw(address vault_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(normalSlot(0));\n }\n\n function getVaultVariables2Raw(address vault_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(normalSlot(1));\n }\n\n function getAbsorbedLiquidityRaw(address vault_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(normalSlot(2));\n }\n\n function getPositionDataRaw(address vault_, uint positionId_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateStorageSlotUintMapping(3, positionId_));\n }\n\n // if tick > 0 then key_ = tick / 256\n // if tick < 0 then key_ = (tick / 256) - 1\n function getTickHasDebtRaw(address vault_, int key_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateStorageSlotIntMapping(4, key_));\n }\n\n function getTickDataRaw(address vault_, int tick_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateStorageSlotIntMapping(5, tick_));\n }\n\n // TODO: Verify below\n // id_ = (realId_ / 3) + 1\n function getTickIdDataRaw(address vault_, int tick_, uint id_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateDoubleIntUintMapping(6, tick_, id_));\n }\n\n function getBranchDataRaw(address vault_, uint branch_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(calculateStorageSlotUintMapping(7, branch_));\n }\n\n function getRateRaw(address vault_) public view returns (uint) {\n return IFluidVaultT1(vault_).readFromStorage(normalSlot(8));\n }\n\n function getRebalancer(address vault_) public view returns (address) {\n return address(uint160(IFluidVaultT1(vault_).readFromStorage(normalSlot(9))));\n }\n\n function getTotalVaults() public view returns (uint) {\n return FACTORY.totalVaults();\n }\n\n function getAllVaultsAddresses() public view returns (address[] memory vaults_) {\n uint totalVaults_ = getTotalVaults();\n vaults_ = new address[](totalVaults_);\n for (uint i = 0; i < totalVaults_; i++) {\n vaults_[i] = getVaultAddress((i + 1));\n }\n }\n\n function _getVaultConstants(address vault_) internal view returns (IFluidVaultT1.ConstantViews memory constants_) {\n constants_ = IFluidVaultT1(vault_).constantsView();\n }\n\n function _getVaultConfig(address vault_) internal view returns (Configs memory configs_) {\n uint vaultVariables2_ = getVaultVariables2Raw(vault_);\n configs_.supplyRateMagnifier = uint16(vaultVariables2_ & X16);\n configs_.borrowRateMagnifier = uint16((vaultVariables2_ >> 16) & X16);\n configs_.collateralFactor = (uint16((vaultVariables2_ >> 32) & X10)) * 10;\n configs_.liquidationThreshold = (uint16((vaultVariables2_ >> 42) & X10)) * 10;\n configs_.liquidationMaxLimit = (uint16((vaultVariables2_ >> 52) & X10) * 10);\n configs_.withdrawalGap = uint16((vaultVariables2_ >> 62) & X10) * 10;\n configs_.liquidationPenalty = uint16((vaultVariables2_ >> 72) & X10);\n configs_.borrowFee = uint16((vaultVariables2_ >> 82) & X10);\n configs_.oracle = address(uint160(vaultVariables2_ >> 96));\n if (configs_.oracle != address(0)) {\n try IFluidOracle(configs_.oracle).getExchangeRateOperate() returns (uint256 exchangeRate_) {\n configs_.oraclePriceOperate = exchangeRate_;\n configs_.oraclePriceLiquidate = IFluidOracle(configs_.oracle).getExchangeRateLiquidate();\n } catch {\n // deprecated backward compatible for older vaults oracles\n configs_.oraclePriceOperate = IFluidOracle(configs_.oracle).getExchangeRate();\n configs_.oraclePriceLiquidate = configs_.oraclePriceOperate;\n }\n }\n configs_.rebalancer = getRebalancer(vault_);\n }\n\n function _getExchangePricesAndRates(\n address vault_,\n Configs memory configs_,\n FluidLiquidityResolverStructs.OverallTokenData memory liquiditySupplytokenData_,\n FluidLiquidityResolverStructs.OverallTokenData memory liquidityBorrowtokenData_\n ) internal view returns (ExchangePricesAndRates memory exchangePricesAndRates_) {\n uint exchangePrices_ = getRateRaw(vault_);\n exchangePricesAndRates_.lastStoredLiquiditySupplyExchangePrice = exchangePrices_ & X64;\n exchangePricesAndRates_.lastStoredLiquidityBorrowExchangePrice = (exchangePrices_ >> 64) & X64;\n exchangePricesAndRates_.lastStoredVaultSupplyExchangePrice = (exchangePrices_ >> 128) & X64;\n exchangePricesAndRates_.lastStoredVaultBorrowExchangePrice = (exchangePrices_ >> 192) & X64;\n\n (\n exchangePricesAndRates_.liquiditySupplyExchangePrice,\n exchangePricesAndRates_.liquidityBorrowExchangePrice,\n exchangePricesAndRates_.vaultSupplyExchangePrice,\n exchangePricesAndRates_.vaultBorrowExchangePrice\n ) = IFluidVaultT1(vault_).updateExchangePrices(getVaultVariables2Raw(vault_));\n\n exchangePricesAndRates_.supplyRateLiquidity = liquiditySupplytokenData_.supplyRate;\n exchangePricesAndRates_.borrowRateLiquidity = liquidityBorrowtokenData_.borrowRate;\n exchangePricesAndRates_.supplyRateVault =\n (liquiditySupplytokenData_.supplyRate * configs_.supplyRateMagnifier) /\n 10000;\n exchangePricesAndRates_.borrowRateVault =\n (liquidityBorrowtokenData_.borrowRate * configs_.borrowRateMagnifier) /\n 10000;\n exchangePricesAndRates_.rewardsRate = configs_.supplyRateMagnifier > 10000\n ? configs_.supplyRateMagnifier - 10000\n : 0;\n }\n\n function _getTotalSupplyAndBorrow(\n address vault_,\n ExchangePricesAndRates memory exchangePricesAndRates_,\n IFluidVaultT1.ConstantViews memory constantsVariables_\n ) internal view returns (TotalSupplyAndBorrow memory totalSupplyAndBorrow_) {\n uint vaultVariables_ = getVaultVariablesRaw(vault_);\n uint absorbedLiquidity_ = getAbsorbedLiquidityRaw(vault_);\n uint totalSupplyLiquidity_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserSupplySlot);\n // extracting user's supply\n totalSupplyLiquidity_ = (totalSupplyLiquidity_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n // converting big number into normal number\n totalSupplyLiquidity_ = (totalSupplyLiquidity_ >> 8) << (totalSupplyLiquidity_ & X8);\n uint totalBorrowLiquidity_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserBorrowSlot);\n // extracting user's borrow\n totalBorrowLiquidity_ = (totalBorrowLiquidity_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64;\n // converting big number into normal number\n totalBorrowLiquidity_ = (totalBorrowLiquidity_ >> 8) << (totalBorrowLiquidity_ & X8);\n\n totalSupplyAndBorrow_.totalSupplyVault = (vaultVariables_ >> 82) & X64;\n // Converting bignumber into normal number\n totalSupplyAndBorrow_.totalSupplyVault =\n (totalSupplyAndBorrow_.totalSupplyVault >> 8) <<\n (totalSupplyAndBorrow_.totalSupplyVault & X8);\n totalSupplyAndBorrow_.totalBorrowVault = (vaultVariables_ >> 146) & X64;\n // Converting bignumber into normal number\n totalSupplyAndBorrow_.totalBorrowVault =\n (totalSupplyAndBorrow_.totalBorrowVault >> 8) <<\n (totalSupplyAndBorrow_.totalBorrowVault & X8);\n\n totalSupplyAndBorrow_.totalSupplyLiquidity = totalSupplyLiquidity_;\n totalSupplyAndBorrow_.totalBorrowLiquidity = totalBorrowLiquidity_;\n\n totalSupplyAndBorrow_.absorbedBorrow = absorbedLiquidity_ & X128;\n totalSupplyAndBorrow_.absorbedSupply = absorbedLiquidity_ >> 128;\n\n // converting raw total supply & total borrow into normal amounts\n totalSupplyAndBorrow_.totalSupplyVault =\n (totalSupplyAndBorrow_.totalSupplyVault * exchangePricesAndRates_.vaultSupplyExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.totalBorrowVault =\n (totalSupplyAndBorrow_.totalBorrowVault * exchangePricesAndRates_.vaultBorrowExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.totalSupplyLiquidity =\n (totalSupplyAndBorrow_.totalSupplyLiquidity * exchangePricesAndRates_.liquiditySupplyExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.totalBorrowLiquidity =\n (totalSupplyAndBorrow_.totalBorrowLiquidity * exchangePricesAndRates_.liquidityBorrowExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.absorbedSupply =\n (totalSupplyAndBorrow_.absorbedSupply * exchangePricesAndRates_.vaultSupplyExchangePrice) /\n 1e12;\n totalSupplyAndBorrow_.absorbedBorrow =\n (totalSupplyAndBorrow_.absorbedBorrow * exchangePricesAndRates_.vaultBorrowExchangePrice) /\n 1e12;\n }\n\n function _getLimitsAndAvailability(\n TotalSupplyAndBorrow memory totalSupplyAndBorrow_,\n ExchangePricesAndRates memory exchangePricesAndRates_,\n IFluidVaultT1.ConstantViews memory constantsVariables_,\n Configs memory configs_,\n FluidLiquidityResolverStructs.UserBorrowData memory liquidityBorrowtokenData_\n ) internal view returns (LimitsAndAvailability memory limitsAndAvailability_) {\n // fetching user's supply slot data\n uint userSupplyLiquidityData_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserSupplySlot);\n if (userSupplyLiquidityData_ > 0) {\n // converting current user's supply from big number to normal\n uint userSupply_ = (userSupplyLiquidityData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n userSupply_ = (userSupply_ >> 8) << (userSupply_ & X8);\n\n // fetching liquidity's withdrawal limit\n uint supplyLimitRaw_ = LiquidityCalcs.calcWithdrawalLimitBeforeOperate(\n userSupplyLiquidityData_,\n userSupply_\n );\n\n limitsAndAvailability_.withdrawLimit =\n (supplyLimitRaw_ * exchangePricesAndRates_.liquiditySupplyExchangePrice) /\n 1e12;\n\n limitsAndAvailability_.withdrawableUntilLimit = totalSupplyAndBorrow_.totalSupplyLiquidity >\n limitsAndAvailability_.withdrawLimit\n ? totalSupplyAndBorrow_.totalSupplyLiquidity - limitsAndAvailability_.withdrawLimit\n : 0;\n uint withdrawalGap_ = (totalSupplyAndBorrow_.totalSupplyLiquidity * configs_.withdrawalGap) / 1e4;\n limitsAndAvailability_.withdrawableUntilLimit = (limitsAndAvailability_.withdrawableUntilLimit >\n withdrawalGap_)\n ? (((limitsAndAvailability_.withdrawableUntilLimit - withdrawalGap_) * 999999) / 1000000)\n : 0;\n\n uint balanceOf_;\n if (constantsVariables_.supplyToken == NATIVE_TOKEN_ADDRESS) {\n balanceOf_ = address(LIQUIDITY).balance;\n } else {\n balanceOf_ = TokenInterface(constantsVariables_.supplyToken).balanceOf(address(LIQUIDITY));\n }\n limitsAndAvailability_.withdrawable = balanceOf_ > limitsAndAvailability_.withdrawableUntilLimit\n ? limitsAndAvailability_.withdrawableUntilLimit\n : balanceOf_;\n }\n\n uint userBorrowLiquidityData_ = LIQUIDITY.readFromStorage(constantsVariables_.liquidityUserBorrowSlot);\n if (userBorrowLiquidityData_ > 0) {\n // converting current user's supply from big number to normal\n uint userBorrow_ = (userBorrowLiquidityData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64;\n userBorrow_ = (userBorrow_ >> 8) << (userBorrow_ & X8);\n\n limitsAndAvailability_.borrowLimit = liquidityBorrowtokenData_.borrowLimit;\n limitsAndAvailability_.borrowLimitUtilization = liquidityBorrowtokenData_.borrowLimitUtilization;\n limitsAndAvailability_.borrowableUntilLimit =\n (liquidityBorrowtokenData_.borrowableUntilLimit * 999999) /\n 1000000;\n\n uint balanceOf_;\n if (constantsVariables_.borrowToken == NATIVE_TOKEN_ADDRESS) {\n balanceOf_ = address(LIQUIDITY).balance;\n } else {\n balanceOf_ = TokenInterface(constantsVariables_.borrowToken).balanceOf(address(LIQUIDITY));\n }\n limitsAndAvailability_.borrowable = balanceOf_ > limitsAndAvailability_.borrowableUntilLimit\n ? limitsAndAvailability_.borrowableUntilLimit\n : balanceOf_;\n }\n\n limitsAndAvailability_.minimumBorrowing = (10001 * exchangePricesAndRates_.vaultBorrowExchangePrice) / 1e12;\n }\n\n function getVaultState(address vault_) public view returns (VaultState memory vaultState_) {\n uint vaultVariables_ = getVaultVariablesRaw(vault_);\n\n vaultState_.topTick = tickHelper(((vaultVariables_ >> 2) & X20));\n vaultState_.currentBranch = (vaultVariables_ >> 22) & X30;\n vaultState_.totalBranch = (vaultVariables_ >> 52) & X30;\n vaultState_.totalSupply = BigMathMinified.fromBigNumber((vaultVariables_ >> 82) & X64, 8, X8);\n vaultState_.totalBorrow = BigMathMinified.fromBigNumber((vaultVariables_ >> 146) & X64, 8, X8);\n vaultState_.totalPositions = (vaultVariables_ >> 210) & X32;\n\n uint currentBranchData_ = getBranchDataRaw(vault_, vaultState_.currentBranch);\n vaultState_.currentBranchState.status = currentBranchData_ & 3;\n vaultState_.currentBranchState.minimaTick = tickHelper(((currentBranchData_ >> 2) & X20));\n vaultState_.currentBranchState.debtFactor = (currentBranchData_ >> 116) & X50;\n vaultState_.currentBranchState.partials = (currentBranchData_ >> 22) & X30;\n vaultState_.currentBranchState.debtLiquidity = BigMathMinified.fromBigNumber(\n (currentBranchData_ >> 52) & X64,\n 8,\n X8\n );\n vaultState_.currentBranchState.baseBranchId = (currentBranchData_ >> 166) & X30;\n vaultState_.currentBranchState.baseBranchMinima = tickHelper(((currentBranchData_ >> 196) & X20));\n }\n\n function getVaultEntireData(address vault_) public view returns (VaultEntireData memory vaultData_) {\n vaultData_.vault = vault_;\n vaultData_.constantVariables = _getVaultConstants(vault_);\n\n (\n FluidLiquidityResolverStructs.UserSupplyData memory liquidityUserSupplyData_,\n FluidLiquidityResolverStructs.OverallTokenData memory liquiditySupplyTokenData_\n ) = LIQUIDITY_RESOLVER.getUserSupplyData(vault_, vaultData_.constantVariables.supplyToken);\n\n (\n FluidLiquidityResolverStructs.UserBorrowData memory liquidityUserBorrowData_,\n FluidLiquidityResolverStructs.OverallTokenData memory liquidityBorrowTokenData_\n ) = LIQUIDITY_RESOLVER.getUserBorrowData(vault_, vaultData_.constantVariables.borrowToken);\n\n vaultData_.configs = _getVaultConfig(vault_);\n vaultData_.exchangePricesAndRates = _getExchangePricesAndRates(\n vault_,\n vaultData_.configs,\n liquiditySupplyTokenData_,\n liquidityBorrowTokenData_\n );\n vaultData_.totalSupplyAndBorrow = _getTotalSupplyAndBorrow(\n vault_,\n vaultData_.exchangePricesAndRates,\n vaultData_.constantVariables\n );\n vaultData_.limitsAndAvailability = _getLimitsAndAvailability(\n vaultData_.totalSupplyAndBorrow,\n vaultData_.exchangePricesAndRates,\n vaultData_.constantVariables,\n vaultData_.configs,\n liquidityUserBorrowData_\n );\n vaultData_.vaultState = getVaultState(vault_);\n\n vaultData_.liquidityUserSupplyData = liquidityUserSupplyData_;\n vaultData_.liquidityUserBorrowData = liquidityUserBorrowData_;\n }\n\n function getVaultsEntireData(\n address[] memory vaults_\n ) external view returns (VaultEntireData[] memory vaultsData_) {\n uint length_ = vaults_.length;\n vaultsData_ = new VaultEntireData[](length_);\n for (uint i = 0; i < length_; i++) {\n vaultsData_[i] = getVaultEntireData(vaults_[i]);\n }\n }\n\n function getVaultsEntireData() external view returns (VaultEntireData[] memory vaultsData_) {\n address[] memory vaults_ = getAllVaultsAddresses();\n uint length_ = vaults_.length;\n vaultsData_ = new VaultEntireData[](length_);\n for (uint i = 0; i < length_; i++) {\n vaultsData_[i] = getVaultEntireData(vaults_[i]);\n }\n }\n\n function positionByNftId(\n uint nftId_\n ) public view returns (UserPosition memory userPosition_, VaultEntireData memory vaultData_) {\n userPosition_.nftId = nftId_;\n address vault_ = vaultByNftId(nftId_);\n if (vault_ != address(0)) {\n uint positionData_ = getPositionDataRaw(vault_, nftId_);\n vaultData_ = getVaultEntireData(vault_);\n\n userPosition_.owner = FACTORY.ownerOf(nftId_);\n userPosition_.isSupplyPosition = (positionData_ & 1) == 1;\n userPosition_.supply = (positionData_ >> 45) & X64;\n // Converting big number into normal number\n userPosition_.supply = (userPosition_.supply >> 8) << (userPosition_.supply & X8);\n userPosition_.beforeSupply = userPosition_.supply;\n userPosition_.dustBorrow = (positionData_ >> 109) & X64;\n // Converting big number into normal number\n userPosition_.dustBorrow = (userPosition_.dustBorrow >> 8) << (userPosition_.dustBorrow & X8);\n userPosition_.beforeDustBorrow = userPosition_.dustBorrow;\n if (!userPosition_.isSupplyPosition) {\n userPosition_.tick = (positionData_ & 2) == 2\n ? int((positionData_ >> 2) & X19)\n : -int((positionData_ >> 2) & X19);\n userPosition_.tickId = (positionData_ >> 21) & X24;\n userPosition_.borrow =\n (TickMath.getRatioAtTick(int24(userPosition_.tick)) * userPosition_.supply) >>\n 96;\n userPosition_.beforeBorrow = userPosition_.borrow - userPosition_.beforeDustBorrow;\n\n uint tickData_ = getTickDataRaw(vault_, userPosition_.tick);\n\n if (((tickData_ & 1) == 1) || (((tickData_ >> 1) & X24) > userPosition_.tickId)) {\n // user got liquidated\n userPosition_.isLiquidated = true;\n (userPosition_.tick, userPosition_.borrow, userPosition_.supply, , ) = IFluidVaultT1(vault_)\n .fetchLatestPosition(userPosition_.tick, userPosition_.tickId, userPosition_.borrow, tickData_);\n }\n\n if (userPosition_.borrow > userPosition_.dustBorrow) {\n userPosition_.borrow = userPosition_.borrow - userPosition_.dustBorrow;\n } else {\n // TODO: Make sure this is right. If borrow is less than dust debt then both gets 0\n userPosition_.borrow = 0;\n userPosition_.dustBorrow = 0;\n }\n }\n\n // converting raw amounts into normal\n userPosition_.beforeSupply =\n (userPosition_.beforeSupply * vaultData_.exchangePricesAndRates.vaultSupplyExchangePrice) /\n 1e12;\n userPosition_.beforeBorrow =\n (userPosition_.beforeBorrow * vaultData_.exchangePricesAndRates.vaultBorrowExchangePrice) /\n 1e12;\n userPosition_.beforeDustBorrow =\n (userPosition_.beforeDustBorrow * vaultData_.exchangePricesAndRates.vaultBorrowExchangePrice) /\n 1e12;\n userPosition_.supply =\n (userPosition_.supply * vaultData_.exchangePricesAndRates.vaultSupplyExchangePrice) /\n 1e12;\n userPosition_.borrow =\n (userPosition_.borrow * vaultData_.exchangePricesAndRates.vaultBorrowExchangePrice) /\n 1e12;\n userPosition_.dustBorrow =\n (userPosition_.dustBorrow * vaultData_.exchangePricesAndRates.vaultBorrowExchangePrice) /\n 1e12;\n }\n }\n\n function positionsNftIdOfUser(address user_) public view returns (uint[] memory nftIds_) {\n uint totalPositions_ = FACTORY.balanceOf(user_);\n nftIds_ = new uint[](totalPositions_);\n for (uint i; i < totalPositions_; i++) {\n nftIds_[i] = FACTORY.tokenOfOwnerByIndex(user_, i);\n }\n }\n\n function vaultByNftId(uint nftId_) public view returns (address vault_) {\n uint tokenConfig_ = getTokenConfig(nftId_);\n vault_ = FACTORY.getVaultAddress((tokenConfig_ >> 192) & X32);\n }\n\n function positionsByUser(\n address user_\n ) external view returns (UserPosition[] memory userPositions_, VaultEntireData[] memory vaultsData_) {\n uint[] memory nftIds_ = positionsNftIdOfUser(user_);\n uint length_ = nftIds_.length;\n userPositions_ = new UserPosition[](length_);\n vaultsData_ = new VaultEntireData[](length_);\n for (uint i = 0; i < length_; i++) {\n (userPositions_[i], vaultsData_[i]) = positionByNftId(nftIds_[i]);\n }\n }\n\n function totalPositions() external view returns (uint) {\n return FACTORY.totalSupply();\n }\n\n /// @dev fetches available liquidations\n /// @param vault_ address of vault for which to fetch\n /// @param tokenInAmt_ token in aka debt to payback, leave 0 to get max\n /// @return liquidationData_ liquidation related data. Check out structs.sol\n function getVaultLiquidation(\n address vault_,\n uint tokenInAmt_\n ) public returns (LiquidationStruct memory liquidationData_) {\n liquidationData_.vault = vault_;\n IFluidVaultT1.ConstantViews memory constants_ = _getVaultConstants(vault_);\n liquidationData_.tokenIn = constants_.borrowToken;\n liquidationData_.tokenOut = constants_.supplyToken;\n\n uint amtOut_;\n uint amtIn_;\n\n tokenInAmt_ = tokenInAmt_ == 0 ? X128 : tokenInAmt_;\n // running without absorb\n try IFluidVaultT1(vault_).liquidate(tokenInAmt_, 0, 0x000000000000000000000000000000000000dEaD, false) {\n // Handle successful execution\n } catch Error(string memory) {\n // Handle generic errors with a reason\n } catch (bytes memory lowLevelData_) {\n // Check if the error data is long enough to contain a selector\n if (lowLevelData_.length >= 68) {\n bytes4 errorSelector_;\n assembly {\n // Extract the selector from the error data\n errorSelector_ := mload(add(lowLevelData_, 0x20))\n }\n if (errorSelector_ == IFluidVaultT1.FluidLiquidateResult.selector) {\n assembly {\n amtOut_ := mload(add(lowLevelData_, 36))\n amtIn_ := mload(add(lowLevelData_, 68))\n }\n liquidationData_.tokenOutAmtOne = amtOut_;\n liquidationData_.tokenInAmtOne = amtIn_;\n } else {\n // tokenInAmtOne & tokenOutAmtOne remains 0\n }\n }\n }\n\n // running with absorb\n try IFluidVaultT1(vault_).liquidate(tokenInAmt_, 0, 0x000000000000000000000000000000000000dEaD, true) {\n // Handle successful execution\n } catch Error(string memory) {\n // Handle generic errors with a reason\n } catch (bytes memory lowLevelData_) {\n // Check if the error data is long enough to contain a selector\n if (lowLevelData_.length >= 68) {\n bytes4 errorSelector_;\n assembly {\n // Extract the selector from the error data\n errorSelector_ := mload(add(lowLevelData_, 0x20))\n }\n if (errorSelector_ == IFluidVaultT1.FluidLiquidateResult.selector) {\n assembly {\n amtOut_ := mload(add(lowLevelData_, 36))\n amtIn_ := mload(add(lowLevelData_, 68))\n }\n liquidationData_.tokenOutAmtTwo = amtOut_;\n liquidationData_.tokenInAmtTwo = amtIn_;\n } else {\n // tokenInAmtTwo & tokenOutAmtTwo remains 0\n }\n }\n }\n }\n\n function getMultipleVaultsLiquidation(\n address[] memory vaults_,\n uint[] memory tokensInAmt_\n ) external returns (LiquidationStruct[] memory liquidationsData_) {\n uint length_ = vaults_.length;\n liquidationsData_ = new LiquidationStruct[](length_);\n for (uint i = 0; i < length_; i++) {\n liquidationsData_[i] = getVaultLiquidation(vaults_[i], tokensInAmt_[i]);\n }\n }\n\n function getAllVaultsLiquidation() external returns (LiquidationStruct[] memory liquidationsData_) {\n address[] memory vaults_ = getAllVaultsAddresses();\n uint length_ = vaults_.length;\n\n liquidationsData_ = new LiquidationStruct[](length_);\n for (uint i = 0; i < length_; i++) {\n liquidationsData_[i] = getVaultLiquidation(vaults_[i], 0);\n }\n }\n\n function getVaultAbsorb(address vault_) public returns (AbsorbStruct memory absorbData_) {\n absorbData_.vault = vault_;\n uint absorbedLiquidity_ = getAbsorbedLiquidityRaw(vault_);\n try IFluidVaultT1(vault_).absorb() {\n // Handle successful execution\n uint newAbsorbedLiquidity_ = getAbsorbedLiquidityRaw(vault_);\n if (newAbsorbedLiquidity_ != absorbedLiquidity_) {\n absorbData_.absorbAvailable = true;\n }\n } catch Error(string memory) {} catch (bytes memory) {}\n }\n\n function getVaultsAbsorb(address[] memory vaults_) public returns (AbsorbStruct[] memory absorbData_) {\n uint length_ = vaults_.length;\n absorbData_ = new AbsorbStruct[](length_);\n for (uint i = 0; i < length_; i++) {\n absorbData_[i] = getVaultAbsorb(vaults_[i]);\n }\n }\n\n function getVaultsAbsorb() public returns (AbsorbStruct[] memory absorbData_) {\n return getVaultsAbsorb(getAllVaultsAddresses());\n }\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/vault/structs.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidVaultT1 } from \"../../../protocols/vault/interfaces/iVaultT1.sol\";\nimport { Structs as FluidLiquidityResolverStructs } from \"../liquidity/structs.sol\";\n\ncontract Structs {\n struct Configs {\n uint16 supplyRateMagnifier;\n uint16 borrowRateMagnifier;\n uint16 collateralFactor;\n uint16 liquidationThreshold;\n uint16 liquidationMaxLimit;\n uint16 withdrawalGap;\n uint16 liquidationPenalty;\n uint16 borrowFee;\n address oracle;\n uint oraclePriceOperate;\n uint oraclePriceLiquidate;\n address rebalancer;\n }\n\n struct ExchangePricesAndRates {\n uint lastStoredLiquiditySupplyExchangePrice;\n uint lastStoredLiquidityBorrowExchangePrice;\n uint lastStoredVaultSupplyExchangePrice;\n uint lastStoredVaultBorrowExchangePrice;\n uint liquiditySupplyExchangePrice;\n uint liquidityBorrowExchangePrice;\n uint vaultSupplyExchangePrice;\n uint vaultBorrowExchangePrice;\n uint supplyRateVault;\n uint borrowRateVault;\n uint supplyRateLiquidity;\n uint borrowRateLiquidity;\n uint rewardsRate; // rewards rate in percent 1e2 precision (1% = 100, 100% = 10000)\n }\n\n struct TotalSupplyAndBorrow {\n uint totalSupplyVault;\n uint totalBorrowVault;\n uint totalSupplyLiquidity;\n uint totalBorrowLiquidity;\n uint absorbedSupply;\n uint absorbedBorrow;\n }\n\n struct LimitsAndAvailability {\n uint withdrawLimit;\n uint withdrawableUntilLimit;\n uint withdrawable;\n uint borrowLimit;\n uint borrowableUntilLimit; // borrowable amount until any borrow limit (incl. max utilization limit)\n uint borrowable; // actual currently borrowable amount (borrow limit - already borrowed) & considering balance, max utilization\n uint borrowLimitUtilization; // borrow limit for `maxUtilization` config at Liquidity\n uint minimumBorrowing;\n }\n\n struct CurrentBranchState {\n uint status; // if 0 then not liquidated, if 1 then liquidated, if 2 then merged, if 3 then closed\n int minimaTick;\n uint debtFactor;\n uint partials;\n uint debtLiquidity;\n uint baseBranchId;\n int baseBranchMinima;\n }\n\n struct VaultState {\n uint totalPositions;\n int topTick;\n uint currentBranch;\n uint totalBranch;\n uint totalBorrow;\n uint totalSupply;\n CurrentBranchState currentBranchState;\n }\n\n struct VaultEntireData {\n address vault;\n IFluidVaultT1.ConstantViews constantVariables;\n Configs configs;\n ExchangePricesAndRates exchangePricesAndRates;\n TotalSupplyAndBorrow totalSupplyAndBorrow;\n LimitsAndAvailability limitsAndAvailability;\n VaultState vaultState;\n // liquidity related data such as supply amount, limits, expansion etc.\n FluidLiquidityResolverStructs.UserSupplyData liquidityUserSupplyData;\n // liquidity related data such as borrow amount, limits, expansion etc.\n FluidLiquidityResolverStructs.UserBorrowData liquidityUserBorrowData;\n }\n\n struct UserPosition {\n uint nftId;\n address owner;\n bool isLiquidated;\n bool isSupplyPosition; // if true that means borrowing is 0\n int tick;\n uint tickId;\n uint beforeSupply;\n uint beforeBorrow;\n uint beforeDustBorrow;\n uint supply;\n uint borrow;\n uint dustBorrow;\n }\n\n /// @dev liquidation related data\n /// @param vault address of vault\n /// @param tokenIn_ address of token in\n /// @param tokenOut_ address of token out\n /// @param tokenInAmtOne_ (without absorb liquidity) minimum of available liquidation & tokenInAmt_\n /// @param tokenOutAmtOne_ (without absorb liquidity) expected token out, collateral to withdraw\n /// @param tokenInAmtTwo_ (absorb liquidity included) minimum of available liquidation & tokenInAmt_. In most cases it'll be same as tokenInAmtOne_ but sometimes can be bigger.\n /// @param tokenOutAmtTwo_ (absorb liquidity included) expected token out, collateral to withdraw. In most cases it'll be same as tokenOutAmtOne_ but sometimes can be bigger.\n /// @dev Liquidity in Two will always be >= One. Sometimes One can provide better swaps, sometimes Two can provide better swaps. But available in Two will always be >= One\n struct LiquidationStruct {\n address vault;\n address tokenIn;\n address tokenOut;\n uint tokenInAmtOne;\n uint tokenOutAmtOne;\n uint tokenInAmtTwo;\n uint tokenOutAmtTwo;\n }\n\n struct AbsorbStruct {\n address vault;\n bool absorbAvailable;\n }\n}\n"
|
||
},
|
||
"contracts/periphery/resolvers/vault/variables.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLiquidityResolver } from \"../liquidity/iLiquidityResolver.sol\";\nimport { IFluidVaultFactory } from \"../../../protocols/vault/interfaces/iVaultFactory.sol\";\n\ninterface IFluidLiquidity {\n function readFromStorage(bytes32 slot_) external view returns (uint256 result_);\n}\n\ncontract Variables {\n IFluidVaultFactory public immutable FACTORY;\n IFluidLiquidity public immutable LIQUIDITY;\n IFluidLiquidityResolver public immutable LIQUIDITY_RESOLVER;\n\n // 30 bits (used for partials mainly)\n uint internal constant X8 = 0xff;\n uint internal constant X10 = 0x3ff;\n uint internal constant X14 = 0x3fff;\n uint internal constant X15 = 0x7fff;\n uint internal constant X16 = 0xffff;\n uint internal constant X19 = 0x7ffff;\n uint internal constant X20 = 0xfffff;\n uint internal constant X24 = 0xffffff;\n uint internal constant X25 = 0x1ffffff;\n uint internal constant X30 = 0x3fffffff;\n uint internal constant X32 = 0xffffffff;\n uint internal constant X35 = 0x7ffffffff;\n uint internal constant X40 = 0xffffffffff;\n uint internal constant X50 = 0x3ffffffffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n uint internal constant X96 = 0xffffffffffffffffffffffff;\n uint internal constant X128 = 0xffffffffffffffffffffffffffffffff;\n /// @dev address that is mapped to the chain native token\n address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n constructor(address factory_, address liquidity_, address liquidityResolver_) {\n FACTORY = IFluidVaultFactory(factory_);\n LIQUIDITY = IFluidLiquidity(liquidity_);\n LIQUIDITY_RESOLVER = IFluidLiquidityResolver(liquidityResolver_);\n }\n}\n"
|
||
},
|
||
"contracts/protocols/lending/error.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Error {\n error FluidLendingError(uint256 errorId_);\n}\n"
|
||
},
|
||
"contracts/protocols/lending/errorTypes.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | fToken | \n |__________________________________*/\n\n /// @notice thrown when a deposit amount is too small to increase BigMath stored balance in Liquidity.\n /// precision of BigMath is 1e12, so if token holds 120_000_000_000 USDC, min amount to make a difference would be 0.1 USDC.\n /// i.e. user would send a very small deposit which mints no shares -> revert\n uint256 internal constant fToken__DepositInsignificant = 20001;\n\n /// @notice thrown when minimum output amount is not reached, e.g. for minimum shares minted (deposit) or\n /// minimum assets received (redeem)\n uint256 internal constant fToken__MinAmountOut = 20002;\n\n /// @notice thrown when maximum amount is surpassed, e.g. for maximum shares burned (withdraw) or\n /// maximum assets input (mint)\n uint256 internal constant fToken__MaxAmount = 20003;\n\n /// @notice thrown when invalid params are sent to a method, e.g. zero address\n uint256 internal constant fToken__InvalidParams = 20004;\n\n /// @notice thrown when an unauthorized caller is trying to execute an auth-protected method\n uint256 internal constant fToken__Unauthorized = 20005;\n\n /// @notice thrown when a with permit / signature method is called from msg.sender that is the owner.\n /// Should call the method without permit instead if msg.sender is the owner.\n uint256 internal constant fToken__PermitFromOwnerCall = 20006;\n\n /// @notice thrown when a reentrancy is detected.\n uint256 internal constant fToken__Reentrancy = 20007;\n\n /// @notice thrown when _tokenExchangePrice overflows type(uint64).max\n uint256 internal constant fToken__ExchangePriceOverflow = 20008;\n\n /// @notice thrown when msg.sender is not rebalancer\n uint256 internal constant fToken__NotRebalancer = 20009;\n\n /// @notice thrown when rebalance is called with msg.value > 0 for non NativeUnderlying fToken\n uint256 internal constant fToken__NotNativeUnderlying = 20010;\n\n /// @notice thrown when the received new liquidity exchange price is of unexpected value (< than the old one)\n uint256 internal constant fToken__LiquidityExchangePriceUnexpected = 20011;\n\n /***********************************|\n | fToken Native Underlying | \n |__________________________________*/\n\n /// @notice thrown when native deposit is called but sent along `msg.value` does not cover the deposit amount\n uint256 internal constant fTokenNativeUnderlying__TransferInsufficient = 21001;\n\n /// @notice thrown when a liquidity callback is called for a native token operation\n uint256 internal constant fTokenNativeUnderlying__UnexpectedLiquidityCallback = 21002;\n\n /***********************************|\n | Lending Factory | \n |__________________________________*/\n\n /// @notice thrown when a method is called with invalid params\n uint256 internal constant LendingFactory__InvalidParams = 22001;\n\n /// @notice thrown when the provided input param address is zero\n uint256 internal constant LendingFactory__ZeroAddress = 22002;\n\n /// @notice thrown when the token already exists\n uint256 internal constant LendingFactory__TokenExists = 22003;\n\n /// @notice thrown when the fToken has not yet been configured at Liquidity\n uint256 internal constant LendingFactory__LiquidityNotConfigured = 22004;\n\n /// @notice thrown when an unauthorized caller is trying to execute an auth-protected method\n uint256 internal constant LendingFactory__Unauthorized = 22005;\n\n /***********************************|\n | Lending Rewards Rate Model | \n |__________________________________*/\n\n /// @notice thrown when invalid params are given as input\n uint256 internal constant LendingRewardsRateModel__InvalidParams = 23001;\n\n /// @notice thrown when calculated rewards rate is exceeding the maximum rate\n uint256 internal constant LendingRewardsRateModel__MaxRate = 23002;\n\n /// @notice thrown when start is called by any other address other than initiator\n uint256 internal constant LendingRewardsRateModel__NotTheInitiator = 23003;\n\n /// @notice thrown when start is called after the rewards are already started\n uint256 internal constant LendingRewardsRateModel__AlreadyStarted = 23004;\n\n /// @notice thrown when the provided input param address is zero\n uint256 internal constant LendingRewardsRateModel__ZeroAddress = 23005;\n}\n"
|
||
},
|
||
"contracts/protocols/lending/fToken/events.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLendingRewardsRateModel } from \"../interfaces/iLendingRewardsRateModel.sol\";\n\nabstract contract Events {\n /// @notice emitted whenever admin updates rewards rate model\n event LogUpdateRewards(IFluidLendingRewardsRateModel indexed rewardsRateModel);\n\n /// @notice emitted whenever rebalance is executed to fund difference between Liquidity deposit and totalAssets()\n /// as rewards through the rebalancer.\n event LogRebalance(uint256 assets);\n\n /// @notice emitted whenever exchange rates are updated\n event LogUpdateRates(uint256 tokenExchangePrice, uint256 liquidityExchangePrice);\n\n /// @notice emitted whenever funds for a certain `token` are rescued to Liquidity\n event LogRescueFunds(address indexed token);\n\n /// @notice emitted whenever rebalancer address is updated\n event LogUpdateRebalancer(address indexed rebalancer);\n}\n"
|
||
},
|
||
"contracts/protocols/lending/fToken/main.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\nimport { FixedPointMathLib } from \"solmate/src/utils/FixedPointMathLib.sol\";\nimport { IERC20Permit } from \"@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IAllowanceTransfer } from \"../interfaces/permit2/iAllowanceTransfer.sol\";\nimport { IFluidLendingRewardsRateModel } from \"../interfaces/iLendingRewardsRateModel.sol\";\nimport { IFluidLendingFactory } from \"../interfaces/iLendingFactory.sol\";\nimport { IFToken, IFTokenAdmin } from \"../interfaces/iFToken.sol\";\nimport { LiquidityCalcs } from \"../../../libraries/liquidityCalcs.sol\";\nimport { BigMathMinified } from \"../../../libraries/bigMathMinified.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { SafeTransfer } from \"../../../libraries/safeTransfer.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { Variables } from \"./variables.sol\";\nimport { Events } from \"./events.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\n/// @dev ReentrancyGuard based on OpenZeppelin implementation.\n/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/security/ReentrancyGuard.sol\nabstract contract ReentrancyGuard is Error, Variables {\n uint8 internal constant REENTRANCY_NOT_ENTERED = 1;\n uint8 internal constant REENTRANCY_ENTERED = 2;\n\n constructor() {\n _status = REENTRANCY_NOT_ENTERED;\n }\n\n /// @dev checks that no reentrancy occurs, reverts if so. Calling the method in the modifier reduces\n /// bytecode size as modifiers are inlined into bytecode\n function _checkReentrancy() internal {\n // On the first call to nonReentrant, _status will be NOT_ENTERED\n if (_status != REENTRANCY_NOT_ENTERED) {\n revert FluidLendingError(ErrorTypes.fToken__Reentrancy);\n }\n\n // Any calls to nonReentrant after this point will fail\n _status = REENTRANCY_ENTERED;\n }\n\n /// @dev Prevents a contract from calling itself, directly or indirectly.\n /// See OpenZeppelin implementation for more info\n modifier nonReentrant() {\n _checkReentrancy();\n\n _;\n\n // storing original value triggers a refund (see https://eips.ethereum.org/EIPS/eip-2200)\n _status = REENTRANCY_NOT_ENTERED;\n }\n}\n\n/// @dev internal methods for fToken contracts\nabstract contract fTokenCore is Error, IERC4626, IFToken, Variables, Events, ReentrancyGuard {\n using FixedPointMathLib for uint256;\n\n /// @dev Gets current (updated) Liquidity supply exchange price for the underyling asset\n function _getLiquidityExchangePrice() internal view returns (uint256 supplyExchangePrice_) {\n (supplyExchangePrice_, ) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_EXCHANGE_PRICES_SLOT)\n );\n }\n\n /// @dev Gets current Liquidity supply balance of `address(this)` for the underyling asset\n function _getLiquidityBalance() internal view returns (uint256 balance_) {\n // extract user supply amount\n uint256 userSupplyRaw_ = BigMathMinified.fromBigNumber(\n (LIQUIDITY.readFromStorage(LIQUIDITY_USER_SUPPLY_SLOT) >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) &\n LiquidityCalcs.X64,\n LiquidityCalcs.DEFAULT_EXPONENT_SIZE,\n LiquidityCalcs.DEFAULT_EXPONENT_MASK\n );\n\n unchecked {\n // can not overflow as userSupplyRaw_ can be maximally type(int128).max, liquidity exchange price type(uint64).max\n return (userSupplyRaw_ * _getLiquidityExchangePrice()) / EXCHANGE_PRICES_PRECISION;\n }\n }\n\n /// @dev Gets current Liquidity underlying token balance\n function _getLiquidityUnderlyingBalance() internal view virtual returns (uint256) {\n return ASSET.balanceOf(address(LIQUIDITY));\n }\n\n /// @dev Gets current withdrawable amount at Liquidity `withdrawalLimit_` (withdrawal limit or balance).\n function _getLiquidityWithdrawable() internal view returns (uint256 withdrawalLimit_) {\n uint256 userSupplyData_ = LIQUIDITY.readFromStorage(LIQUIDITY_USER_SUPPLY_SLOT);\n uint256 userSupply_ = BigMathMinified.fromBigNumber(\n (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & LiquidityCalcs.X64,\n LiquidityCalcs.DEFAULT_EXPONENT_SIZE,\n LiquidityCalcs.DEFAULT_EXPONENT_MASK\n );\n withdrawalLimit_ = LiquidityCalcs.calcWithdrawalLimitBeforeOperate(userSupplyData_, userSupply_);\n\n // convert raw amounts to normal amounts\n unchecked {\n // can not overflow as userSupply_ can be maximally type(int128).max\n // and withdrawalLimit is smaller than userSupply_\n uint256 liquidityExchangePrice_ = _getLiquidityExchangePrice();\n withdrawalLimit_ = (withdrawalLimit_ * liquidityExchangePrice_) / EXCHANGE_PRICES_PRECISION;\n userSupply_ = (userSupply_ * liquidityExchangePrice_) / EXCHANGE_PRICES_PRECISION;\n }\n\n withdrawalLimit_ = userSupply_ > withdrawalLimit_ ? userSupply_ - withdrawalLimit_ : 0;\n\n uint256 balanceAtLiquidity_ = _getLiquidityUnderlyingBalance();\n\n return balanceAtLiquidity_ > withdrawalLimit_ ? withdrawalLimit_ : balanceAtLiquidity_;\n }\n\n /// @dev Calculates new token exchange price based on the current liquidity exchange price `newLiquidityExchangePrice_` and rewards rate.\n /// @param newLiquidityExchangePrice_ new (current) liquidity exchange price\n function _calculateNewTokenExchangePrice(\n uint256 newLiquidityExchangePrice_\n ) internal view returns (uint256 newTokenExchangePrice_, bool rewardsEnded_) {\n uint256 oldTokenExchangePrice_ = _tokenExchangePrice;\n uint256 oldLiquidityExchangePrice_ = _liquidityExchangePrice;\n\n if (newLiquidityExchangePrice_ < oldLiquidityExchangePrice_) {\n // liquidity exchange price should only ever increase. If not, something went wrong and avoid\n // proceeding with unknown outcome.\n revert FluidLendingError(ErrorTypes.fToken__LiquidityExchangePriceUnexpected);\n }\n\n uint256 totalReturnInPercent_; // rewardsRateInPercent + liquidityReturnInPercent\n if (_rewardsActive) {\n {\n // get rewards rate per year\n // only trigger call to rewardsRateModel if rewards are actually active to save gas\n uint256 rewardsRate_;\n uint256 rewardsStartTime_;\n (rewardsRate_, rewardsEnded_, rewardsStartTime_) = _rewardsRateModel.getRate(\n // use old tokenExchangeRate to calculate the total assets input for the rewards rate\n (oldTokenExchangePrice_ * totalSupply()) / EXCHANGE_PRICES_PRECISION\n );\n\n if (rewardsRate_ > MAX_REWARDS_RATE || rewardsEnded_) {\n // rewardsRate is capped, if it is bigger > MAX_REWARDS_RATE, then the rewardsRateModel\n // is configured wrongly (which should not be possible). Setting rewards to 0 in that case here.\n rewardsRate_ = 0;\n }\n\n uint256 lastUpdateTimestamp_ = _lastUpdateTimestamp;\n if (lastUpdateTimestamp_ < rewardsStartTime_) {\n // if last update was before the rewards started, make sure rewards actually only accrue\n // from the actual rewards start time, not from the last update timestamp to avoid overpayment.\n lastUpdateTimestamp_ = rewardsStartTime_;\n\n // Note: overpayment for block.timestamp being > rewards end time does not happen because\n // rewardsRate_ is forced 0 then.\n }\n\n // calculate rewards return in percent: (rewards_rate * time passed) / seconds_in_a_year.\n unchecked {\n // rewardsRate * timeElapsed / SECONDS_PER_YEAR.\n // no safe checks needed here because timeElapsed can not underflow,\n // rewardsRate is in 1e12 at max value being MAX_REWARDS_RATE = 25e12\n // max value would be 25e12 * 8589934591 / 31536000 (with buffers) = 6.8e15\n totalReturnInPercent_ =\n (rewardsRate_ * (block.timestamp - lastUpdateTimestamp_)) /\n SECONDS_PER_YEAR;\n }\n }\n }\n\n unchecked {\n // calculate liquidityReturnInPercent: (newLiquidityExchangePrice_ - oldLiquidityExchangePrice_) / oldLiquidityExchangePrice_.\n // and add it to totalReturnInPercent_ that already holds rewardsRateInPercent_.\n // max value (in absolute extreme unrealistic case) would be: 6.8e15 + (((max uint64 - 1e12) * 1e12) / 1e12) = 1.845e19\n // oldLiquidityExchangePrice_ can not be 0, minimal value is 1e12. subtraction can not underflow because new exchange price\n // can only be >= oldLiquidityExchangePrice_.\n totalReturnInPercent_ +=\n ((newLiquidityExchangePrice_ - oldLiquidityExchangePrice_) * 1e14) /\n oldLiquidityExchangePrice_;\n }\n\n // newTokenExchangePrice_ = oldTokenExchangePrice_ + oldTokenExchangePrice_ * totalReturnInPercent_\n newTokenExchangePrice_ = oldTokenExchangePrice_ + ((oldTokenExchangePrice_ * totalReturnInPercent_) / 1e14); // divided by 100% (1e14)\n }\n\n /// @dev calculates new exchange prices, updates values in storage and returns new tokenExchangePrice (with reward rates)\n function _updateRates(\n uint256 liquidityExchangePrice_,\n bool forceUpdateStorage_\n ) internal returns (uint256 tokenExchangePrice_) {\n bool rewardsEnded_;\n (tokenExchangePrice_, rewardsEnded_) = _calculateNewTokenExchangePrice(liquidityExchangePrice_);\n if (_rewardsActive || forceUpdateStorage_) {\n // Solidity will NOT cause a revert if values are too big to fit max uint type size. Explicitly check before\n // writing to storage. Also see https://github.com/ethereum/solidity/issues/10195.\n if (tokenExchangePrice_ > type(uint64).max) {\n revert FluidLendingError(ErrorTypes.fToken__ExchangePriceOverflow);\n }\n\n _tokenExchangePrice = uint64(tokenExchangePrice_);\n _liquidityExchangePrice = uint64(liquidityExchangePrice_);\n _lastUpdateTimestamp = uint40(block.timestamp);\n\n emit LogUpdateRates(tokenExchangePrice_, liquidityExchangePrice_);\n }\n\n if (rewardsEnded_) {\n // set rewardsActive flag to false to save gas for all future exchange prices calculations,\n // without having to explicitly require setting `updateRewards` to address zero.\n // Note that it would be fine that even the current tx does not update exchange prices in storage,\n // because if rewardsEnded_ is true, rewardsRate_ must be 0, so the only yield is from LIQUIDITY.\n // But to be extra safe, writing to storage in that one case too before setting _rewardsActive to false.\n _rewardsActive = false;\n }\n\n return tokenExchangePrice_;\n }\n\n /// @dev splits a bytes signature `sig` into `v`, `r`, `s`.\n /// Taken from https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html\n function _splitSignature(bytes memory sig) internal pure returns (uint8 v, bytes32 r, bytes32 s) {\n require(sig.length == 65);\n\n assembly {\n // first 32 bytes, after the length prefix.\n r := mload(add(sig, 32))\n // second 32 bytes.\n s := mload(add(sig, 64))\n // final byte (first byte of the next 32 bytes).\n v := byte(0, mload(add(sig, 96)))\n }\n\n return (v, r, s);\n }\n\n /// @dev Deposit `assets_` amount of tokens to Liquidity\n /// @param assets_ The amount of tokens to deposit\n /// @param liquidityCallbackData_ callback data passed to Liquidity for `liquidityCallback`\n /// @return exchangePrice_ liquidity exchange price for token\n function _depositToLiquidity(\n uint256 assets_,\n bytes memory liquidityCallbackData_\n ) internal virtual returns (uint256 exchangePrice_) {\n // @dev Note: Although there might be some small difference between the `assets_` amount and the actual amount\n // accredited at Liquidity due to BigMath rounding down, this amount is so small that it can be ignored.\n // because of BigMath precision of 7.2057594e16 for a coefficient size of 56, it would require >72 trillion DAI\n // to \"benefit\" 1 DAI in additional shares minted. Considering gas cost + APR per second, this ensures such\n // a manipulation attempt becomes extremely unlikely.\n\n // send funds to Liquidity protocol to generate yield\n (exchangePrice_, ) = LIQUIDITY.operate(\n address(ASSET),\n SafeCast.toInt256(assets_),\n 0,\n address(0),\n address(0),\n liquidityCallbackData_ // callback data. -> \"from\" for transferFrom in `liquidityCallback`\n );\n }\n\n /// @dev Withdraw `assets_` amount of tokens from Liquidity directly to `receiver_`\n /// @param assets_ The amount of tokens to withdraw\n /// @param receiver_ the receiver address of withdraw amount\n /// @return exchangePrice_ liquidity exchange price for token\n function _withdrawFromLiquidity(\n uint256 assets_,\n address receiver_\n ) internal virtual returns (uint256 exchangePrice_) {\n // @dev See similar comment in `_depositToLiquidity()` regarding burning a tiny bit of additional shares here\n // because of inaccuracies in Liquidity userSupply BigMath being rounded down.\n\n // get funds back from Liquidity protocol to send to the user\n (exchangePrice_, ) = LIQUIDITY.operate(\n address(ASSET),\n -SafeCast.toInt256(assets_),\n 0,\n receiver_,\n address(0),\n new bytes(0) // callback data -> withdraw doesn't trigger a callback\n );\n }\n\n /// @dev deposits `assets_` into liquidity and mints shares for `receiver_`. Returns amount of `sharesMinted_`.\n function _executeDeposit(\n uint256 assets_,\n address receiver_,\n bytes memory liquidityCallbackData_\n ) internal virtual validAddress(receiver_) returns (uint256 sharesMinted_) {\n // send funds to Liquidity protocol to generate yield -> returns updated liquidityExchangePrice\n uint256 tokenExchangePrice_ = _depositToLiquidity(assets_, liquidityCallbackData_);\n\n // update the exchange prices\n tokenExchangePrice_ = _updateRates(tokenExchangePrice_, false);\n\n // calculate the shares to mint\n // not using previewDeposit here because we just got newTokenExchangePrice_\n sharesMinted_ = (assets_ * EXCHANGE_PRICES_PRECISION) / tokenExchangePrice_;\n\n if (sharesMinted_ == 0) {\n revert FluidLendingError(ErrorTypes.fToken__DepositInsignificant);\n }\n\n _mint(receiver_, sharesMinted_);\n\n emit Deposit(msg.sender, receiver_, assets_, sharesMinted_);\n }\n\n /// @dev withdraws `assets_` from liquidity to `receiver_` and burns shares from `owner_`.\n /// Returns amount of `sharesBurned_`.\n /// requires nonReentrant! modifier on calling method otherwise ERC777s could reenter!\n function _executeWithdraw(\n uint256 assets_,\n address receiver_,\n address owner_\n ) internal virtual validAddress(receiver_) returns (uint256 sharesBurned_) {\n // burn shares for assets_ amount: assets_ * EXCHANGE_PRICES_PRECISION / updatedTokenTexchangePrice. Rounded up.\n // Note to be extra safe we do the shares burn before the withdrawFromLiquidity, even though that would return the\n // updated liquidityExchangePrice and thus save gas.\n sharesBurned_ = assets_.mulDivUp(EXCHANGE_PRICES_PRECISION, _updateRates(_getLiquidityExchangePrice(), false));\n\n /*\n The `mulDivUp` function is designed to round up the result of multiplication followed by division. \n Given non-zero `assets_` and the rounding-up behavior of this function, `sharesBurned_` will always \n be at least 1 if there's any remainder in the division.\n Thus, if `assets_` is non-zero, `sharesBurned_` can never be 0. The nature of the function ensures \n that even the smallest fractional result (greater than 0) will be rounded up to 1. Hence, there's no need \n to check for a rounding error that results in 0.\n Furthermore, if `assets_` was 0, an error 'UserModule__OperateAmountsZero' would already have been thrown \n during the `operate` function, ensuring the contract never reaches this point with a zero `assets_` value.\n Note: If ever the logic or the function behavior changes in the future, this assertion may need to be reconsidered.\n */\n\n _burn(owner_, sharesBurned_);\n\n // withdraw from liquidity directly to _receiver.\n _withdrawFromLiquidity(assets_, receiver_);\n\n emit Withdraw(msg.sender, receiver_, owner_, assets_, sharesBurned_);\n }\n}\n\n/// @notice fToken view methods. Implements view methods for ERC4626 compatibility\nabstract contract fTokenViews is fTokenCore {\n using FixedPointMathLib for uint256;\n\n /// @inheritdoc IFToken\n function getData()\n public\n view\n returns (\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IFluidLendingRewardsRateModel lendingRewardsRateModel_,\n IAllowanceTransfer permit2_,\n address rebalancer_,\n bool rewardsActive_,\n uint256 liquidityBalance_,\n uint256 liquidityExchangePrice_,\n uint256 tokenExchangePrice_\n )\n {\n liquidityExchangePrice_ = _getLiquidityExchangePrice();\n\n bool rewardsEnded_;\n (tokenExchangePrice_, rewardsEnded_) = _calculateNewTokenExchangePrice(liquidityExchangePrice_);\n\n return (\n LIQUIDITY,\n LENDING_FACTORY,\n _rewardsRateModel,\n PERMIT2,\n _rebalancer,\n _rewardsActive && !rewardsEnded_,\n _getLiquidityBalance(),\n liquidityExchangePrice_,\n tokenExchangePrice_\n );\n }\n\n /// @inheritdoc IERC4626\n function asset() public view virtual override returns (address) {\n return address(ASSET);\n }\n\n /// @inheritdoc IERC4626\n function totalAssets() public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return\n // all the underlying tokens are stored in Liquidity contract at all times\n (tokenExchangePrice_ * totalSupply()) / EXCHANGE_PRICES_PRECISION;\n }\n\n /// @inheritdoc IERC4626\n function convertToShares(uint256 assets_) public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return assets_.mulDivDown(EXCHANGE_PRICES_PRECISION, tokenExchangePrice_);\n }\n\n /// @inheritdoc IERC4626\n function convertToAssets(uint256 shares_) public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return shares_.mulDivDown(tokenExchangePrice_, EXCHANGE_PRICES_PRECISION);\n }\n\n /// @inheritdoc IERC4626\n /// @notice returned amount might be slightly different from actual amount at execution.\n function previewDeposit(uint256 assets_) public view virtual override returns (uint256) {\n return convertToShares(assets_);\n }\n\n /// @inheritdoc IERC4626\n function previewMint(uint256 shares_) public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return shares_.mulDivUp(tokenExchangePrice_, EXCHANGE_PRICES_PRECISION);\n }\n\n /// @inheritdoc IERC4626\n function previewWithdraw(uint256 assets_) public view virtual override returns (uint256) {\n (uint256 tokenExchangePrice_, ) = _calculateNewTokenExchangePrice(_getLiquidityExchangePrice());\n return assets_.mulDivUp(EXCHANGE_PRICES_PRECISION, tokenExchangePrice_);\n }\n\n /// @inheritdoc IERC4626\n /// @notice returned amount might be slightly different from actual amount at execution.\n function previewRedeem(uint256 shares_) public view virtual override returns (uint256) {\n return convertToAssets(shares_);\n }\n\n /*//////////////////////////////////////////////////////////////\n DEPOSIT/WITHDRAWAL LIMIT LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n function maxDeposit(address) public view virtual override returns (uint256) {\n // read total supplyInterest_ for the token at Liquidity and convert from BigMath\n uint256 supplyInterest_ = LIQUIDITY.readFromStorage(LIQUIDITY_TOTAL_AMOUNTS_SLOT) & LiquidityCalcs.X64;\n supplyInterest_ =\n (supplyInterest_ >> LiquidityCalcs.DEFAULT_EXPONENT_SIZE) <<\n (supplyInterest_ & LiquidityCalcs.DEFAULT_EXPONENT_MASK);\n\n unchecked {\n // normalize from raw\n supplyInterest_ = (supplyInterest_ * _getLiquidityExchangePrice()) / EXCHANGE_PRICES_PRECISION;\n // compare against hardcoded max possible value for total supply considering BigMath rounding down:\n // type(int128).max) after BigMath rounding (first 56 bits precision, then 71 bits getting set to 0)\n // so 1111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000\n // = 170141183460469229370504062281061498880. using minus 1\n if (supplyInterest_ > 170141183460469229370504062281061498879) {\n return 0;\n }\n // type(int128).max is the maximum interactable amount at Liquidity. But also total token amounts\n // must not overflow type(int128).max, so max depositable is type(int128).max - totalSupply.\n return uint256(uint128(type(int128).max)) - supplyInterest_;\n }\n }\n\n /// @inheritdoc IERC4626\n function maxMint(address) public view virtual override returns (uint256) {\n return convertToShares(maxDeposit(address(0)));\n }\n\n /// @inheritdoc IERC4626\n function maxWithdraw(address owner_) public view virtual override returns (uint256) {\n uint256 maxWithdrawableAtLiquidity_ = _getLiquidityWithdrawable();\n uint256 ownerBalance_ = convertToAssets(balanceOf(owner_));\n return maxWithdrawableAtLiquidity_ < ownerBalance_ ? maxWithdrawableAtLiquidity_ : ownerBalance_;\n }\n\n /// @inheritdoc IERC4626\n function maxRedeem(address owner_) public view virtual override returns (uint256) {\n uint256 maxWithdrawableAtLiquidity_ = convertToShares(_getLiquidityWithdrawable());\n uint256 ownerBalance_ = balanceOf(owner_);\n return maxWithdrawableAtLiquidity_ < ownerBalance_ ? maxWithdrawableAtLiquidity_ : ownerBalance_;\n }\n\n /// @inheritdoc IFToken\n function minDeposit() public view returns (uint256) {\n uint256 minBigMathRounding_ = 1 <<\n (LIQUIDITY.readFromStorage(LIQUIDITY_TOTAL_AMOUNTS_SLOT) & LiquidityCalcs.DEFAULT_EXPONENT_MASK); // 1 << total supply exponent\n uint256 previewMint_ = previewMint(1); // rounds up\n return minBigMathRounding_ > previewMint_ ? minBigMathRounding_ : previewMint_;\n }\n}\n\n/// @notice fToken admin related methods. fToken admins are Lending Factory auths. Possible actions are\n/// updating rewards, funding rewards, and rescuing any stuck funds (fToken contract itself never holds any funds).\nabstract contract fTokenAdmin is fTokenCore, fTokenViews {\n /// @dev checks if `msg.sender` is an allowed auth at LendingFactory. internal method instead of modifier\n /// to reduce bytecode size.\n function _checkIsLendingFactoryAuth() internal view {\n if (!LENDING_FACTORY.isAuth(msg.sender)) {\n revert FluidLendingError(ErrorTypes.fToken__Unauthorized);\n }\n }\n\n /// @inheritdoc IFTokenAdmin\n function updateRewards(IFluidLendingRewardsRateModel rewardsRateModel_) external {\n _checkIsLendingFactoryAuth();\n\n // @dev no check for address zero needed here, as that is actually explicitly checked where _rewardsRateModel\n // is used. In fact it is beneficial to set _rewardsRateModel to address zero when there are no rewards.\n\n // apply current rewards rate before updating to new one\n updateRates();\n\n _rewardsRateModel = rewardsRateModel_;\n\n // set flag _rewardsActive\n _rewardsActive = address(rewardsRateModel_) != address(0);\n\n emit LogUpdateRewards(rewardsRateModel_);\n }\n\n /// @inheritdoc IFTokenAdmin\n function rebalance() external payable virtual nonReentrant returns (uint256 assets_) {\n if (msg.sender != _rebalancer) {\n revert FluidLendingError(ErrorTypes.fToken__NotRebalancer);\n }\n if (msg.value > 0) {\n revert FluidLendingError(ErrorTypes.fToken__NotNativeUnderlying);\n }\n // calculating difference in assets. if liquidity balance is bigger it'll throw which is an expected behaviour\n assets_ = totalAssets() - _getLiquidityBalance();\n // send funds to Liquidity protocol\n uint256 liquidityExchangePrice_ = _depositToLiquidity(assets_, abi.encode(msg.sender));\n\n // update the exchange prices, always updating on storage\n _updateRates(liquidityExchangePrice_, true);\n\n // no shares are minted when funding fToken contract for rewards\n\n emit LogRebalance(assets_);\n }\n\n /// @inheritdoc IFTokenAdmin\n function updateRebalancer(address newRebalancer_) public validAddress(newRebalancer_) {\n _checkIsLendingFactoryAuth();\n\n _rebalancer = newRebalancer_;\n\n emit LogUpdateRebalancer(newRebalancer_);\n }\n\n /// @inheritdoc IFTokenAdmin\n function updateRates() public returns (uint256 tokenExchangePrice_, uint256 liquidityExchangePrice_) {\n liquidityExchangePrice_ = _getLiquidityExchangePrice();\n tokenExchangePrice_ = _updateRates(liquidityExchangePrice_, true);\n }\n\n /// @inheritdoc IFTokenAdmin\n //\n // @dev this contract never holds any funds:\n // -> deposited funds are directly sent to Liquidity.\n // -> rewards are also stored at Liquidity.\n function rescueFunds(address token_) external virtual nonReentrant {\n _checkIsLendingFactoryAuth();\n SafeTransfer.safeTransfer(address(token_), address(LIQUIDITY), IERC20(token_).balanceOf(address(this)));\n emit LogRescueFunds(token_);\n }\n}\n\n/// @notice fToken public executable actions: deposit, mint, mithdraw and redeem.\n/// All actions are optionally also available with an additional param to limit the maximum slippage, e.g. maximum\n/// assets used for minting x amount of shares.\nabstract contract fTokenActions is fTokenCore, fTokenViews {\n /// @dev reverts if `amount_` is < `minAmountOut_`. Used to reduce bytecode size.\n function _revertIfBelowMinAmountOut(uint256 amount_, uint256 minAmountOut_) internal pure {\n if (amount_ < minAmountOut_) {\n revert FluidLendingError(ErrorTypes.fToken__MinAmountOut);\n }\n }\n\n /// @dev reverts if `amount_` is > `maxAmount_`. Used to reduce bytecode size.\n function _revertIfAboveMaxAmount(uint256 amount_, uint256 maxAmount_) internal pure {\n if (amount_ > maxAmount_) {\n revert FluidLendingError(ErrorTypes.fToken__MaxAmount);\n }\n }\n\n /*//////////////////////////////////////////////////////////////\n DEPOSIT\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n /// @notice If `assets_` equals uint256.max then the whole balance of `msg.sender` is deposited.\n /// `assets_` must at least be `minDeposit()` amount; reverts `fToken__DepositInsignificant()` if not.\n /// Recommended to use `deposit()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return shares_ actually minted shares\n function deposit(\n uint256 assets_,\n address receiver_\n ) public virtual override nonReentrant returns (uint256 shares_) {\n if (assets_ == type(uint256).max) {\n assets_ = ASSET.balanceOf(msg.sender);\n }\n\n // @dev transfer of tokens from `msg.sender` to liquidity contract happens via `liquidityCallback`\n shares_ = _executeDeposit(assets_, receiver_, abi.encode(msg.sender));\n }\n\n /// @notice same as {fToken-deposit} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached\n function deposit(uint256 assets_, address receiver_, uint256 minAmountOut_) external returns (uint256 shares_) {\n shares_ = deposit(assets_, receiver_);\n _revertIfBelowMinAmountOut(shares_, minAmountOut_);\n }\n\n /*//////////////////////////////////////////////////////////////\n MINT \n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n /// @notice If `shares_` equals uint256.max then the whole balance of `msg.sender` is deposited.\n /// `shares_` must at least be `minMint()` amount; reverts `fToken__DepositInsignificant()` if not.\n /// Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.\n /// Recommended to use `deposit()` over mint because it is more gas efficient and less likely to revert.\n /// Recommended to use `mint()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return assets_ deposited assets amount\n function mint(uint256 shares_, address receiver_) public virtual override nonReentrant returns (uint256 assets_) {\n if (shares_ == type(uint256).max) {\n assets_ = ASSET.balanceOf(msg.sender);\n } else {\n // No need to check for rounding error, previewMint rounds up.\n assets_ = previewMint(shares_);\n }\n\n // @dev transfer of tokens from `msg.sender` to liquidity contract happens via `liquidityCallback`\n\n _executeDeposit(assets_, receiver_, abi.encode(msg.sender));\n }\n\n /// @notice same as {fToken-mint} but with an additional setting for maximum assets input amount.\n /// reverts with `fToken__MaxAmount()` if `maxAssets_` of assets is surpassed to mint `shares_`.\n function mint(uint256 shares_, address receiver_, uint256 maxAssets_) external returns (uint256 assets_) {\n assets_ = mint(shares_, receiver_);\n _revertIfAboveMaxAmount(assets_, maxAssets_);\n }\n\n /*//////////////////////////////////////////////////////////////\n WITHDRAW\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n /// @notice If `assets_` equals uint256.max then the whole fToken balance of `owner_` is withdrawn. This does not\n /// consider withdrawal limit at Liquidity so best to check with `maxWithdraw()` before.\n /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.\n /// Recommended to use `withdraw()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return shares_ burned shares\n function withdraw(\n uint256 assets_,\n address receiver_,\n address owner_\n ) public virtual override nonReentrant returns (uint256 shares_) {\n if (assets_ == type(uint256).max) {\n assets_ = previewRedeem(balanceOf(owner_));\n }\n shares_ = _executeWithdraw(assets_, receiver_, owner_);\n\n if (msg.sender != owner_) {\n _spendAllowance(owner_, msg.sender, shares_);\n }\n }\n\n /// @notice same as {fToken-withdraw} but with an additional setting for maximum shares burned.\n /// reverts with `fToken__MaxAmount()` if `maxSharesBurn_` of shares burned is surpassed.\n function withdraw(\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_\n ) external returns (uint256 shares_) {\n shares_ = withdraw(assets_, receiver_, owner_);\n _revertIfAboveMaxAmount(shares_, maxSharesBurn_);\n }\n\n /*//////////////////////////////////////////////////////////////\n REDEEM\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IERC4626\n /// @notice If `shares_` equals uint256.max then the whole balance of `owner_` is withdrawn.This does not\n /// consider withdrawal limit at Liquidity so best to check with `maxRedeem()` before.\n /// Recommended to use `withdraw()` over redeem because it is more gas efficient and can set specific amount.\n /// Recommended to use `redeem()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return assets_ withdrawn assets amount\n function redeem(\n uint256 shares_,\n address receiver_,\n address owner_\n ) public virtual override nonReentrant returns (uint256 assets_) {\n if (shares_ == type(uint256).max) {\n shares_ = balanceOf(owner_);\n }\n\n assets_ = previewRedeem(shares_);\n\n uint256 burnedShares_ = _executeWithdraw(assets_, receiver_, owner_);\n\n if (msg.sender != owner_) {\n _spendAllowance(owner_, msg.sender, burnedShares_);\n }\n }\n\n /// @notice same as {fToken-redeem} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of assets is not reached.\n function redeem(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_\n ) external returns (uint256 assets_) {\n assets_ = redeem(shares_, receiver_, owner_);\n _revertIfBelowMinAmountOut(assets_, minAmountOut_);\n }\n}\n\n/// @notice fTokens support EIP-2612 permit approvals via signature so this contract implements\n/// withdrawals (withdraw / redeem) with signature used for approval of the fToken shares.\nabstract contract fTokenEIP2612Withdrawals is fTokenActions {\n /// @dev creates `sharesToPermit_` allowance for `owner_` via EIP2612 `deadline_` and `signature_`\n function _allowViaPermitEIP2612(\n address owner_,\n uint256 sharesToPermit_,\n uint256 deadline_,\n bytes calldata signature_\n ) internal {\n (uint8 v_, bytes32 r_, bytes32 s_) = _splitSignature(signature_);\n // spender = msg.sender\n permit(owner_, msg.sender, sharesToPermit_, deadline_, v_, r_, s_);\n }\n\n /// @notice withdraw amount of `assets_` with ERC-2612 permit signature for fToken approval.\n /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.\n /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.\n /// allowance via signature (`sharesToPermit_`) should cover `previewWithdraw(assets_)` plus a little buffer to avoid revert.\n /// Inherent trust assumption that `msg.sender` will set `receiver_` and `maxSharesBurn_` as `owner_` intends\n /// (which is always the case when giving allowance to some spender).\n /// @param sharesToPermit_ shares amount to use for EIP2612 permit(). Should cover `previewWithdraw(assets_)` + small buffer.\n /// @param assets_ amount of assets to withdraw\n /// @param receiver_ receiver of withdrawn assets\n /// @param owner_ owner to withdraw from (must be signature signer)\n /// @param maxSharesBurn_ maximum accepted amount of shares burned\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for ERC-2612 permit\n /// @return shares_ burned shares amount\n function withdrawWithSignature(\n uint256 sharesToPermit_,\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_,\n uint256 deadline_,\n bytes calldata signature_\n ) external virtual nonReentrant returns (uint256 shares_) {\n if (msg.sender == owner_) {\n // no sense in operating with permit if msg.sender is owner. should call normal `withdraw()` instead.\n revert FluidLendingError(ErrorTypes.fToken__PermitFromOwnerCall);\n }\n\n // create allowance through signature_\n _allowViaPermitEIP2612(owner_, sharesToPermit_, deadline_, signature_);\n\n // execute withdraw to get shares_ to spend amount\n shares_ = _executeWithdraw(assets_, receiver_, owner_);\n\n _revertIfAboveMaxAmount(shares_, maxSharesBurn_);\n\n _spendAllowance(owner_, msg.sender, shares_);\n }\n\n /// @notice redeem amount of `shares_` with ERC-2612 permit signature for fToken approval.\n /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.\n /// Note there might be tiny inaccuracies between requested `shares_` to redeem and actually burned shares.\n /// allowance via signature must cover `shares_` plus a tiny buffer.\n /// Inherent trust assumption that `msg.sender` will set `receiver_` and `minAmountOut_` as `owner_` intends\n /// (which is always the case when giving allowance to some spender).\n /// Recommended to use `withdraw()` over redeem because it is more gas efficient and can set specific amount.\n /// @param shares_ amount of shares to redeem\n /// @param receiver_ receiver of withdrawn assets\n /// @param owner_ owner to withdraw from (must be signature signer)\n /// @param minAmountOut_ minimum accepted amount of assets withdrawn\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for ERC-2612 permit\n /// @return assets_ withdrawn assets amount\n function redeemWithSignature(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_,\n uint256 deadline_,\n bytes calldata signature_\n ) external virtual nonReentrant returns (uint256 assets_) {\n if (msg.sender == owner_) {\n // no sense in operating with permit if msg.sender is owner. should call normal `redeem()` instead.\n revert FluidLendingError(ErrorTypes.fToken__PermitFromOwnerCall);\n }\n\n assets_ = previewRedeem(shares_);\n _revertIfBelowMinAmountOut(assets_, minAmountOut_);\n\n // create allowance through signature_\n _allowViaPermitEIP2612(owner_, shares_, deadline_, signature_);\n\n // execute withdraw to get actual shares to spend amount\n uint256 sharesToSpend_ = _executeWithdraw(assets_, receiver_, owner_);\n\n _spendAllowance(owner_, msg.sender, sharesToSpend_);\n }\n}\n\n/// @notice implements fTokens support for deposit / mint via EIP-2612 permit.\n/// @dev methods revert if underlying asset does not support EIP-2612.\nabstract contract fTokenEIP2612Deposits is fTokenActions {\n /// @notice deposit `assets_` amount with EIP-2612 Permit2 signature for underlying asset approval.\n /// IMPORTANT: This will revert if the underlying `asset()` does not support EIP-2612.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached.\n /// `assets_` must at least be `minDeposit()` amount; reverts `fToken__DepositInsignificant()` if not.\n /// @param assets_ amount of assets to deposit\n /// @param receiver_ receiver of minted fToken shares\n /// @param minAmountOut_ minimum accepted amount of shares minted\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for EIP-2612 Permit\n /// @return shares_ amount of minted shares\n function depositWithSignatureEIP2612(\n uint256 assets_,\n address receiver_,\n uint256 minAmountOut_,\n uint256 deadline_,\n bytes calldata signature_\n ) external returns (uint256 shares_) {\n // create allowance through signature_ and spend it\n (uint8 v_, bytes32 r_, bytes32 s_) = _splitSignature(signature_);\n\n // EIP-2612 permit for underlying asset from owner (msg.sender) to spender (this contract)\n IERC20Permit(address(ASSET)).permit(msg.sender, address(this), assets_, deadline_, v_, r_, s_);\n\n // deposit() includes nonReentrant modifier which is enough to have from this point forward\n shares_ = deposit(assets_, receiver_);\n _revertIfBelowMinAmountOut(shares_, minAmountOut_);\n }\n\n /// @notice mint amount of `shares_` with EIP-2612 Permit signature for underlying asset approval.\n /// IMPORTANT: This will revert if the underlying `asset()` does not support EIP-2612.\n /// Signature should approve a little bit more than expected assets amount (`previewMint()`) to avoid reverts.\n /// `shares_` must at least be `minMint()` amount; reverts with `fToken__DepositInsignificant()` if not.\n /// Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.\n /// Recommended to use `deposit()` over mint because it is more gas efficient and less likely to revert.\n /// @param shares_ amount of shares to mint\n /// @param receiver_ receiver of minted fToken shares\n /// @param maxAssets_ maximum accepted amount of assets used as input to mint `shares_`\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for EIP-2612 Permit\n /// @return assets_ deposited assets amount\n function mintWithSignatureEIP2612(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_,\n uint256 deadline_,\n bytes calldata signature_\n ) external returns (uint256 assets_) {\n assets_ = previewMint(shares_);\n\n // create allowance through signature_ and spend it\n (uint8 v_, bytes32 r_, bytes32 s_) = _splitSignature(signature_);\n\n // EIP-2612 permit for underlying asset from owner (msg.sender) to spender (this contract)\n IERC20Permit(address(ASSET)).permit(msg.sender, address(this), assets_, deadline_, v_, r_, s_);\n\n // mint() includes nonReentrant modifier which is enough to have from this point forward\n assets_ = mint(shares_, receiver_);\n _revertIfAboveMaxAmount(assets_, maxAssets_);\n }\n}\n\n/// @notice implements fTokens support for deposit / mint via Permit2 signature.\nabstract contract fTokenPermit2Deposits is fTokenActions {\n /// @inheritdoc IFToken\n function depositWithSignature(\n uint256 assets_,\n address receiver_,\n uint256 minAmountOut_,\n IAllowanceTransfer.PermitSingle calldata permit_,\n bytes calldata signature_\n ) external nonReentrant returns (uint256 shares_) {\n // give allowance to address(this) via Permit2 signature -> to spend allowance in LiquidityCallback\n // to transfer funds directly from msg.sender to liquidity\n PERMIT2.permit(\n // owner - Who signed the permit and also holds the tokens\n // @dev Note if this is modified to not be msg.sender, extra steps would be needed for security!\n // the caller could use this signature and deposit to the balance of receiver_, which could be set to any address,\n // because it is not included in the signature. Use permitWitnessTransferFrom in that case. Same for `minAmountOut_`.\n msg.sender,\n permit_, // permit message\n signature_ // packed signature of signing the EIP712 hash of `permit_`\n );\n\n // @dev transfer of tokens from `msg.sender` to liquidity contract happens via `liquidityCallback`\n\n shares_ = _executeDeposit(assets_, receiver_, abi.encode(true, msg.sender));\n _revertIfBelowMinAmountOut(shares_, minAmountOut_);\n }\n\n /// @inheritdoc IFToken\n function mintWithSignature(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_,\n IAllowanceTransfer.PermitSingle calldata permit_,\n bytes calldata signature_\n ) external nonReentrant returns (uint256 assets_) {\n assets_ = previewMint(shares_);\n _revertIfAboveMaxAmount(assets_, maxAssets_);\n\n // give allowance to address(this) via Permit2 PermitSingle. to spend allowance in LiquidityCallback\n // to transfer funds directly from msg.sender to liquidity\n PERMIT2.permit(\n // owner - Who signed the permit and also holds the tokens\n // @dev Note if this is modified to not be msg.sender, extra steps would be needed for security!\n // the caller could use this signature and deposit to the balance of receiver_, which could be set to any address,\n // because it is not included in the signature. Use permitWitnessTransferFrom in that case. Same for `minAmountOut_`.\n msg.sender,\n permit_, // permit message\n signature_ // packed signature of signing the EIP712 hash of `permit_`\n );\n\n // @dev transfer of tokens from `msg.sender` to liquidity contract happens via `liquidityCallback`\n\n _executeDeposit(assets_, receiver_, abi.encode(true, msg.sender));\n }\n}\n\n/// @title Fluid fToken (Lending with interest)\n/// @notice fToken is a token that can be used to supply liquidity to the Fluid Liquidity pool and earn interest for doing so.\n/// The fToken is backed by the underlying balance and can be redeemed for the underlying token at any time.\n/// The interest is earned via Fluid Liquidity, e.g. because borrowers pay a borrow rate on it. In addition, fTokens may also\n/// have active rewards going on that count towards the earned yield for fToken holders.\n/// @dev The fToken implements the ERC20 and ERC4626 standard, which means it can be transferred, minted and burned.\n/// The fToken supports EIP-2612 permit approvals via signature.\n/// The fToken implements withdrawals via EIP-2612 permits and deposits with Permit2 or EIP-2612 (if underlying supports it) signatures.\n/// fTokens are not upgradeable.\n/// @dev For view methods / accessing data, use the \"LendingResolver\" periphery contract.\n//\n// fTokens can only be deployed for underlying tokens that are listed at Liquidity (`_getLiquidityExchangePrice()` reverts\n// otherwise, which is called in the constructor).\ncontract fToken is fTokenAdmin, fTokenActions, fTokenEIP2612Withdrawals, fTokenPermit2Deposits, fTokenEIP2612Deposits {\n /// @param liquidity_ liquidity contract address\n /// @param lendingFactory_ lending factory contract address\n /// @param asset_ underlying token address\n constructor(\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IERC20 asset_\n ) Variables(liquidity_, lendingFactory_, asset_) {\n // set initial values for _liquidityExchangePrice, _tokenExchangePrice and _lastUpdateTimestamp\n _liquidityExchangePrice = uint64(_getLiquidityExchangePrice());\n _tokenExchangePrice = uint64(EXCHANGE_PRICES_PRECISION);\n _lastUpdateTimestamp = uint40(block.timestamp);\n }\n\n /// @inheritdoc IERC20Metadata\n function decimals() public view virtual override(ERC20, IERC20Metadata) returns (uint8) {\n return DECIMALS;\n }\n\n /// @inheritdoc IFToken\n function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external virtual override {\n if (msg.sender != address(LIQUIDITY) || token_ != address(ASSET) || _status != REENTRANCY_ENTERED) {\n // caller must be liquidity, token must match, and reentrancy status must be REENTRANCY_ENTERED\n revert FluidLendingError(ErrorTypes.fToken__Unauthorized);\n }\n\n // callback data can be a) an address only b) an address + transfer via permit2 flag set to true\n // for a) length will be 32, for b) length is 64\n if (data_.length == 32) {\n address from_ = abi.decode(data_, (address));\n\n // transfer `amount_` from `from_` (original deposit msg.sender) to liquidity contract\n SafeTransfer.safeTransferFrom(address(ASSET), from_, address(LIQUIDITY), amount_);\n } else {\n (bool isPermit2_, address from_) = abi.decode(data_, (bool, address));\n if (!isPermit2_) {\n // unexepcted liquidity callback data\n revert FluidLendingError(ErrorTypes.fToken__InvalidParams);\n }\n\n // transfer `amount_` from `from_` (original deposit msg.sender) to liquidity contract via PERMIT2\n PERMIT2.transferFrom(from_, address(LIQUIDITY), uint160(amount_), address(ASSET));\n }\n }\n}\n"
|
||
},
|
||
"contracts/protocols/lending/fToken/nativeUnderlying/fTokenNativeUnderlying.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\nimport { FixedPointMathLib } from \"solmate/src/utils/FixedPointMathLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { SafeTransfer } from \"../../../../libraries/safeTransfer.sol\";\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { fTokenCore, fTokenAdmin, fToken } from \"../main.sol\";\n\nimport { IWETH9 } from \"../../interfaces/external/iWETH9.sol\";\nimport { IFluidLendingFactory } from \"../../interfaces/iLendingFactory.sol\";\nimport { IFTokenAdmin, IFTokenNativeUnderlying, IFToken } from \"../../interfaces/iFToken.sol\";\nimport { IFluidLiquidity } from \"../../../../liquidity/interfaces/iLiquidity.sol\";\n\n/// @dev overrides certain methods from the inherited fToken used as base contract to make them compatible with\n/// the native token being used as underlying.\nabstract contract fTokenNativeUnderlyingOverrides is fToken, IFTokenNativeUnderlying {\n using FixedPointMathLib for uint256;\n\n /// @inheritdoc IFTokenNativeUnderlying\n address public constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @dev gets asset address for liquidity slot links, overridden to set native token address\n function _getLiquiditySlotLinksAsset() internal view virtual override returns (address) {\n return NATIVE_TOKEN_ADDRESS;\n }\n\n /// @dev Gets current Liquidity underlying token balance\n function _getLiquidityUnderlyingBalance() internal view virtual override returns (uint256) {\n return address(LIQUIDITY).balance;\n }\n\n /// @inheritdoc IFTokenAdmin\n function rescueFunds(address token_) external virtual override(IFTokenAdmin, fTokenAdmin) nonReentrant {\n _checkIsLendingFactoryAuth();\n\n if (token_ == NATIVE_TOKEN_ADDRESS) {\n Address.sendValue(payable(address(LIQUIDITY)), address(this).balance);\n } else {\n SafeTransfer.safeTransfer(address(token_), address(LIQUIDITY), IERC20(token_).balanceOf(address(this)));\n }\n\n emit LogRescueFunds(token_);\n }\n\n /*//////////////////////////////////////////////////////////////\n REWARDS\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc fTokenAdmin\n function rebalance()\n external\n payable\n virtual\n override(IFTokenAdmin, fTokenAdmin)\n nonReentrant\n returns (uint256 assets_)\n {\n if (msg.sender != _rebalancer) {\n revert FluidLendingError(ErrorTypes.fToken__NotRebalancer);\n }\n // calculating difference in assets. if liquidity balance is bigger it'll throw which is an expected behaviour\n assets_ = totalAssets() - _getLiquidityBalance();\n\n if (msg.value < assets_) {\n assets_ = msg.value;\n } else if (msg.value > assets_) {\n // send back overfunded msg.value amount\n Address.sendValue(payable(msg.sender), msg.value - assets_);\n }\n\n // send funds to Liquidity protocol to generate yield\n uint256 liquidityExchangePrice_ = _depositToLiquidity(assets_, new bytes(0));\n\n // update the exchange prices, always updating on storage\n _updateRates(liquidityExchangePrice_, true);\n\n // no shares are minted when funding fToken contract for rewards\n\n emit LogRebalance(assets_);\n }\n\n /*//////////////////////////////////////////////////////////////\n DEPOSIT\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc fTokenCore\n function _depositToLiquidity(\n uint256 assets_,\n bytes memory liquidityCallbackData_\n ) internal virtual override returns (uint256 exchangePrice_) {\n // send funds to Liquidity protocol to generate yield, send along msg.value\n (exchangePrice_, ) = LIQUIDITY.operate{ value: assets_ }(\n NATIVE_TOKEN_ADDRESS, // deposit to Liquidity is always in native, also if user input is wrapped token\n SafeCast.toInt256(assets_),\n 0,\n address(0),\n address(0),\n liquidityCallbackData_ // callback data. -> \"from\" for transferFrom in `liquidityCallback`\n );\n }\n\n /// @inheritdoc fTokenCore\n function _executeDeposit(\n uint256 assets_,\n address receiver_,\n // liquidityCallbackData_ not needed for native transfer, sent along as msg.value. But used to recognize Permit2 transfers.\n bytes memory liquidityCallbackData_\n ) internal virtual override returns (uint256 sharesMinted_) {\n // transfer wrapped asset from user to this contract\n if (liquidityCallbackData_.length > 32) {\n // liquidityCallbackData_ with length > 32 can only be Permit2 as all others maximally encode from address\n PERMIT2.transferFrom(msg.sender, address(this), uint160(assets_), address(ASSET));\n } else {\n SafeTransfer.safeTransferFrom(address(ASSET), msg.sender, address(this), assets_);\n }\n\n // convert WETH to native underlying token\n IWETH9(address(ASSET)).withdraw(assets_);\n\n // super._executeDeposit includes check for validAddress receiver_\n return super._executeDeposit(assets_, receiver_, new bytes(0));\n }\n\n /// @dev deposits `msg.value` amount of native token into liquidity and mints shares for `receiver_`.\n /// Returns amount of `sharesMinted_`.\n function _executeDepositNative(address receiver_) internal virtual returns (uint256 sharesMinted_) {\n // super._executeDeposit includes check for validAddress receiver_\n return super._executeDeposit(msg.value, receiver_, new bytes(0));\n }\n\n /*//////////////////////////////////////////////////////////////\n WITHDRAW\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc fTokenCore\n function _withdrawFromLiquidity(\n uint256 assets_,\n address receiver_\n ) internal virtual override returns (uint256 exchangePrice_) {\n // get funds back from Liquidity protocol to send to the user\n (exchangePrice_, ) = LIQUIDITY.operate(\n NATIVE_TOKEN_ADDRESS, // withdraw from Liquidity is always in native, also if user output is wrapped token\n -SafeCast.toInt256(assets_),\n 0,\n receiver_,\n address(0),\n new bytes(0) // callback data -> withdraw doesn't trigger a callback\n );\n }\n\n /// @inheritdoc fTokenCore\n function _executeWithdraw(\n uint256 assets_,\n address receiver_,\n address owner_\n ) internal virtual override returns (uint256 sharesBurned_) {\n // super._executeWithdraw includes check for validAddress(receiver_)\n\n // withdraw from liquidity to this contract first to convert withdrawn native token to wrapped native for _receiver.\n sharesBurned_ = super._executeWithdraw(assets_, address(this), owner_);\n\n // convert received native underlying token to WETH and transfer to receiver_\n IWETH9(address(ASSET)).deposit{ value: assets_ }();\n SafeTransfer.safeTransfer(address(ASSET), receiver_, assets_);\n }\n\n /// @dev withdraws `assets_` from liquidity to `receiver_` and burns shares from `owner_`.\n /// Returns amount of `sharesBurned_`.\n function _executeWithdrawNative(\n uint256 assets_,\n address receiver_,\n address owner_\n ) internal virtual returns (uint256 sharesBurned_) {\n // super._executeWithdraw includes check for validAddress(receiver_)\n return super._executeWithdraw(assets_, receiver_, owner_);\n }\n}\n\n/// @notice implements deposit / mint / withdraw / redeem actions with Native token being used as interaction token.\nabstract contract fTokenNativeUnderlyingActions is fTokenNativeUnderlyingOverrides {\n /*//////////////////////////////////////////////////////////////\n DEPOSIT\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFTokenNativeUnderlying\n function depositNative(address receiver_) public payable nonReentrant returns (uint256 shares_) {\n shares_ = _executeDepositNative(receiver_);\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function depositNative(address receiver_, uint256 minAmountOut_) external payable returns (uint256 shares_) {\n shares_ = depositNative(receiver_);\n _revertIfBelowMinAmountOut(shares_, minAmountOut_);\n }\n\n /*//////////////////////////////////////////////////////////////\n MINT \n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFTokenNativeUnderlying\n function mintNative(uint256 shares_, address receiver_) public payable nonReentrant returns (uint256 assets_) {\n // No need to check for rounding error, previewMint rounds up.\n assets_ = previewMint(shares_);\n\n if (msg.value < assets_) {\n // not enough msg.value sent along to cover mint shares amount\n revert FluidLendingError(ErrorTypes.fTokenNativeUnderlying__TransferInsufficient);\n }\n\n _executeDepositNative(receiver_);\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function mintNative(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_\n ) external payable returns (uint256 assets_) {\n assets_ = mintNative(shares_, receiver_);\n _revertIfAboveMaxAmount(assets_, maxAssets_);\n }\n\n /*//////////////////////////////////////////////////////////////\n WITHDRAW\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFTokenNativeUnderlying\n function withdrawNative(\n uint256 assets_,\n address receiver_,\n address owner_\n ) public nonReentrant returns (uint256 shares_) {\n if (assets_ == type(uint256).max) {\n assets_ = previewRedeem(balanceOf(msg.sender));\n }\n\n shares_ = _executeWithdrawNative(assets_, receiver_, owner_);\n\n if (msg.sender != owner_) {\n _spendAllowance(owner_, msg.sender, shares_);\n }\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function withdrawNative(\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_\n ) external returns (uint256 shares_) {\n shares_ = withdrawNative(assets_, receiver_, owner_);\n _revertIfAboveMaxAmount(shares_, maxSharesBurn_);\n }\n\n /*//////////////////////////////////////////////////////////////\n REDEEM\n //////////////////////////////////////////////////////////////*/\n\n /// @inheritdoc IFTokenNativeUnderlying\n function redeemNative(\n uint256 shares_,\n address receiver_,\n address owner_\n ) public nonReentrant returns (uint256 assets_) {\n if (shares_ == type(uint256).max) {\n shares_ = balanceOf(msg.sender);\n }\n\n assets_ = previewRedeem(shares_);\n\n uint256 burnedShares_ = _executeWithdrawNative(assets_, receiver_, owner_);\n\n if (msg.sender != owner_) {\n _spendAllowance(owner_, msg.sender, burnedShares_);\n }\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function redeemNative(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_\n ) external returns (uint256 assets_) {\n assets_ = redeemNative(shares_, receiver_, owner_);\n _revertIfBelowMinAmountOut(assets_, minAmountOut_);\n }\n}\n\n/// @notice fTokens support EIP-2612 permit approvals via signature so withdrawals are possible with signature.\n/// This contract implements those withdrawals for a native underlying asset.\nabstract contract fTokenNativeUnderlyingEIP2612Withdrawals is fTokenNativeUnderlyingActions {\n /// @inheritdoc IFTokenNativeUnderlying\n function withdrawWithSignatureNative(\n uint256 sharesToPermit_,\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_,\n uint256 deadline_,\n bytes calldata signature_\n ) external nonReentrant returns (uint256 shares_) {\n // @dev logic below is exactly the same as in {fTokenEIP2612Withdrawals-withdrawWithSignature}, just using\n // _executeWithdrawNative instead of _executeWithdraw\n\n if (msg.sender == owner_) {\n // no sense in operating with permit if msg.sender is owner. should call normal `withdraw()` instead.\n revert FluidLendingError(ErrorTypes.fToken__PermitFromOwnerCall);\n }\n\n // create allowance through signature_\n _allowViaPermitEIP2612(owner_, sharesToPermit_, deadline_, signature_);\n\n // execute withdraw to get shares_ to spend amount\n shares_ = _executeWithdrawNative(assets_, receiver_, owner_);\n\n _revertIfAboveMaxAmount(shares_, maxSharesBurn_);\n\n _spendAllowance(owner_, msg.sender, shares_);\n }\n\n /// @inheritdoc IFTokenNativeUnderlying\n function redeemWithSignatureNative(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_,\n uint256 deadline_,\n bytes calldata signature_\n ) external nonReentrant returns (uint256 assets_) {\n // @dev logic below is exactly the same as in {fTokenEIP2612Withdrawals-redeemWithSignature}, just using\n // _executeWithdrawNative instead of _executeWithdraw\n\n if (msg.sender == owner_) {\n // no sense in operating with permit if msg.sender is owner. should call normal `redeem()` instead.\n revert FluidLendingError(ErrorTypes.fToken__PermitFromOwnerCall);\n }\n\n assets_ = previewRedeem(shares_);\n _revertIfBelowMinAmountOut(assets_, minAmountOut_);\n\n // create allowance through signature_\n _allowViaPermitEIP2612(owner_, shares_, deadline_, signature_);\n\n // execute withdraw to get actual shares to spend amount\n uint256 sharesToSpend_ = _executeWithdrawNative(assets_, receiver_, owner_);\n\n _spendAllowance(owner_, msg.sender, sharesToSpend_);\n }\n}\n\n/// @notice Same as the {fToken} contract but with support for native token as underlying asset.\n/// Actual underlying asset is the wrapped native ERC20 version (e.g. WETH), which acts like any other fToken.\n/// But in addition the fTokenNativeUnderlying also has methods for doing all the same actions via the native token.\ncontract fTokenNativeUnderlying is fTokenNativeUnderlyingEIP2612Withdrawals {\n /// @param liquidity_ liquidity contract address\n /// @param lendingFactory_ lending factory contract address\n /// @param weth_ address of wrapped native token (e.g. WETH)\n constructor(\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IWETH9 weth_\n ) fToken(liquidity_, lendingFactory_, IERC20(address(weth_))) {}\n\n /// @inheritdoc fToken\n function liquidityCallback(\n address /** token_ */,\n uint256 /** amount_ */,\n bytes calldata /** data_ */\n ) external virtual override(IFToken, fToken) {\n // not needed because msg.value is used directly\n revert FluidLendingError(ErrorTypes.fTokenNativeUnderlying__UnexpectedLiquidityCallback);\n }\n\n receive() external payable {}\n}\n"
|
||
},
|
||
"contracts/protocols/lending/fToken/variables.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { ERC20Permit } from \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\n\nimport { IAllowanceTransfer } from \"../interfaces/permit2/iAllowanceTransfer.sol\";\nimport { LiquiditySlotsLink } from \"../../../libraries/liquiditySlotsLink.sol\";\nimport { IFToken } from \"../interfaces/iFToken.sol\";\nimport { IAllowanceTransfer } from \"../interfaces/permit2/iAllowanceTransfer.sol\";\nimport { IFluidLendingRewardsRateModel } from \"../interfaces/iLendingRewardsRateModel.sol\";\nimport { IFluidLendingFactory } from \"../interfaces/iLendingFactory.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\nabstract contract Constants {\n /// @dev permit2 contract, deployed to same address on EVM networks, see https://github.com/Uniswap/permit2\n IAllowanceTransfer internal constant PERMIT2 = IAllowanceTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3);\n\n /// @dev precision for exchange prices\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n\n /// @dev max allowed reward rate is 50%\n uint256 internal constant MAX_REWARDS_RATE = 50 * 1e12; // 50%;\n\n /// @dev address of the Liquidity contract.\n IFluidLiquidity internal immutable LIQUIDITY;\n\n /// @dev address of the Lending factory contract.\n IFluidLendingFactory internal immutable LENDING_FACTORY;\n\n /// @dev address of the underlying asset contract.\n IERC20 internal immutable ASSET;\n\n /// @dev number of decimals for the fToken, same as ASSET\n uint8 internal immutable DECIMALS;\n\n /// @dev slot ids in Liquidity contract for underlying token.\n /// Helps in low gas fetch from liquidity contract by skipping delegate call with `readFromStorage`\n bytes32 internal immutable LIQUIDITY_EXCHANGE_PRICES_SLOT;\n bytes32 internal immutable LIQUIDITY_TOTAL_AMOUNTS_SLOT;\n bytes32 internal immutable LIQUIDITY_USER_SUPPLY_SLOT;\n\n /// @param liquidity_ liquidity contract address\n /// @param lendingFactory_ lending factory contract address\n /// @param asset_ underlying token address\n constructor(IFluidLiquidity liquidity_, IFluidLendingFactory lendingFactory_, IERC20 asset_) {\n DECIMALS = IERC20Metadata(address(asset_)).decimals();\n ASSET = asset_;\n LIQUIDITY = liquidity_;\n LENDING_FACTORY = lendingFactory_;\n\n LIQUIDITY_EXCHANGE_PRICES_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n _getLiquiditySlotLinksAsset()\n );\n LIQUIDITY_TOTAL_AMOUNTS_SLOT = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n _getLiquiditySlotLinksAsset()\n );\n LIQUIDITY_USER_SUPPLY_SLOT = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT,\n address(this),\n _getLiquiditySlotLinksAsset()\n );\n }\n\n /// @dev gets asset address for liquidity slot links, extracted to separate method so it can be overridden if needed\n function _getLiquiditySlotLinksAsset() internal view virtual returns (address) {\n return address(ASSET);\n }\n}\n\nabstract contract Variables is ERC20, ERC20Permit, Error, Constants, IFToken {\n /// @dev prefix for token name. fToken will append the underlying asset name\n string private constant TOKEN_NAME_PREFIX = \"Fluid \";\n /// @dev prefix for token symbol. fToken will append the underlying asset symbol\n string private constant TOKEN_SYMBOL_PREFIX = \"f\";\n\n // ------------ storage variables from inherited contracts come before vars here --------\n // _________ ERC20 _______________\n // ----------------------- slot 0 ---------------------------\n // mapping(address => uint256) private _balances;\n\n // ----------------------- slot 1 ---------------------------\n // mapping(address => mapping(address => uint256)) private _allowances;\n\n // ----------------------- slot 2 ---------------------------\n // uint256 private _totalSupply;\n\n // ----------------------- slot 3 ---------------------------\n // string private _name;\n // ----------------------- slot 4 ---------------------------\n // string private _symbol;\n\n // _________ ERC20Permit _______________\n // ----------------------- slot 5 ---------------------------\n // mapping(address => Counters.Counter) private _nonces;\n\n // ----------------------- slot 6 ---------------------------\n // bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;\n\n // ----------------------- slot 7 ---------------------------\n /// @dev address of the LendingRewardsRateModel.\n IFluidLendingRewardsRateModel internal _rewardsRateModel;\n\n // -> 12 bytes empty\n uint96 private __placeholder_gap;\n\n // ----------------------- slot 8 ---------------------------\n // optimized to put all storage variables where a SSTORE happens on actions in the same storage slot\n\n /// @dev exchange price for the underlying assset in the liquidity protocol (without rewards)\n uint64 internal _liquidityExchangePrice; // in 1e12 -> (max value 18_446_744,073709551615)\n\n /// @dev exchange price between fToken and the underlying assset (with rewards)\n uint64 internal _tokenExchangePrice; // in 1e12 -> (max value 18_446_744,073709551615)\n\n /// @dev timestamp when exchange prices were updated the last time\n uint40 internal _lastUpdateTimestamp;\n\n /// @dev status for reentrancy guard\n uint8 internal _status;\n\n /// @dev flag to signal if rewards are active without having to read slot 6\n bool internal _rewardsActive;\n\n // 72 bits empty (9 bytes)\n\n // ----------------------- slot 9 ---------------------------\n /// @dev rebalancer address allowed to call `rebalance()` and source for funding rewards (ReserveContract).\n address internal _rebalancer;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n /// @param liquidity_ liquidity contract address\n /// @param lendingFactory_ lending factory contract address\n /// @param asset_ underlying token address\n constructor(\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IERC20 asset_\n )\n validAddress(address(liquidity_))\n validAddress(address(lendingFactory_))\n validAddress(address(asset_))\n Constants(liquidity_, lendingFactory_, asset_)\n ERC20(\n string(abi.encodePacked(TOKEN_NAME_PREFIX, IERC20Metadata(address(asset_)).name())),\n string(abi.encodePacked(TOKEN_SYMBOL_PREFIX, IERC20Metadata(address(asset_)).symbol()))\n )\n ERC20Permit(string(abi.encodePacked(TOKEN_NAME_PREFIX, IERC20Metadata(address(asset_)).name())))\n {}\n\n /// @dev checks that address is not the zero address, reverts if so. Calling the method in the modifier reduces\n /// bytecode size as modifiers are inlined into bytecode\n function _checkValidAddress(address value_) internal pure {\n if (value_ == address(0)) {\n revert FluidLendingError(ErrorTypes.fToken__InvalidParams);\n }\n }\n\n /// @dev validates that an address is not the zero address\n modifier validAddress(address value_) {\n _checkValidAddress(value_);\n _;\n }\n}\n"
|
||
},
|
||
"contracts/protocols/lending/interfaces/external/iWETH9.sol": {
|
||
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IWETH9 is IERC20 {\n function deposit() external payable;\n\n function withdraw(uint256 wad) external;\n}\n"
|
||
},
|
||
"contracts/protocols/lending/interfaces/iFToken.sol": {
|
||
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC4626 } from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\n\nimport { IAllowanceTransfer } from \"./permit2/iAllowanceTransfer.sol\";\nimport { IFluidLendingRewardsRateModel } from \"./iLendingRewardsRateModel.sol\";\nimport { IFluidLendingFactory } from \"./iLendingFactory.sol\";\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\n\ninterface IFTokenAdmin {\n /// @notice updates the rewards rate model contract.\n /// Only callable by LendingFactory auths.\n /// @param rewardsRateModel_ the new rewards rate model contract address.\n /// can be set to address(0) to set no rewards (to save gas)\n function updateRewards(IFluidLendingRewardsRateModel rewardsRateModel_) external;\n\n /// @notice Balances out the difference between fToken supply at Liquidity vs totalAssets().\n /// Deposits underlying from rebalancer address into Liquidity but doesn't mint any shares\n /// -> thus making deposit available as rewards.\n /// Only callable by rebalancer.\n /// @return assets_ amount deposited to Liquidity\n function rebalance() external payable returns (uint256 assets_);\n\n /// @notice gets the liquidity exchange price of the underlying asset, calculates the updated exchange price (with reward rates)\n /// and writes those values to storage.\n /// Callable by anyone.\n /// @return tokenExchangePrice_ exchange price of fToken share to underlying asset\n /// @return liquidityExchangePrice_ exchange price at Liquidity for the underlying asset\n function updateRates() external returns (uint256 tokenExchangePrice_, uint256 liquidityExchangePrice_);\n\n /// @notice sends any potentially stuck funds to Liquidity contract. Only callable by LendingFactory auths.\n function rescueFunds(address token_) external;\n\n /// @notice Updates the rebalancer address (ReserveContract). Only callable by LendingFactory auths.\n function updateRebalancer(address rebalancer_) external;\n}\n\ninterface IFToken is IERC4626, IFTokenAdmin {\n /// @notice returns minimum amount required for deposit (rounded up)\n function minDeposit() external view returns (uint256);\n\n /// @notice returns config, rewards and exchange prices data in a single view method.\n /// @return liquidity_ address of the Liquidity contract.\n /// @return lendingFactory_ address of the Lending factory contract.\n /// @return lendingRewardsRateModel_ address of the rewards rate model contract. changeable by LendingFactory auths.\n /// @return permit2_ address of the Permit2 contract used for deposits / mint with signature\n /// @return rebalancer_ address of the rebalancer allowed to execute `rebalance()`\n /// @return rewardsActive_ true if rewards are currently active\n /// @return liquidityBalance_ current Liquidity supply balance of `address(this)` for the underyling asset\n /// @return liquidityExchangePrice_ (updated) exchange price for the underlying assset in the liquidity protocol (without rewards)\n /// @return tokenExchangePrice_ (updated) exchange price between fToken and the underlying assset (with rewards)\n function getData()\n external\n view\n returns (\n IFluidLiquidity liquidity_,\n IFluidLendingFactory lendingFactory_,\n IFluidLendingRewardsRateModel lendingRewardsRateModel_,\n IAllowanceTransfer permit2_,\n address rebalancer_,\n bool rewardsActive_,\n uint256 liquidityBalance_,\n uint256 liquidityExchangePrice_,\n uint256 tokenExchangePrice_\n );\n\n /// @notice transfers `amount_` of `token_` to liquidity. Only callable by liquidity contract.\n /// @dev this callback is used to optimize gas consumption (reducing necessary token transfers).\n function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external;\n\n /// @notice deposit `assets_` amount with Permit2 signature for underlying asset approval.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached.\n /// `assets_` must at least be `minDeposit()` amount; reverts otherwise.\n /// @param assets_ amount of assets to deposit\n /// @param receiver_ receiver of minted fToken shares\n /// @param minAmountOut_ minimum accepted amount of shares minted\n /// @param permit_ Permit2 permit message\n /// @param signature_ packed signature of signing the EIP712 hash of `permit_`\n /// @return shares_ amount of minted shares\n function depositWithSignature(\n uint256 assets_,\n address receiver_,\n uint256 minAmountOut_,\n IAllowanceTransfer.PermitSingle calldata permit_,\n bytes calldata signature_\n ) external returns (uint256 shares_);\n\n /// @notice mint amount of `shares_` with Permit2 signature for underlying asset approval.\n /// Signature should approve a little bit more than expected assets amount (`previewMint()`) to avoid reverts.\n /// `shares_` must at least be `minMint()` amount; reverts otherwise.\n /// Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.\n /// Recommended to use `deposit()` over mint because it is more gas efficient and less likely to revert.\n /// @param shares_ amount of shares to mint\n /// @param receiver_ receiver of minted fToken shares\n /// @param maxAssets_ maximum accepted amount of assets used as input to mint `shares_`\n /// @param permit_ Permit2 permit message\n /// @param signature_ packed signature of signing the EIP712 hash of `permit_`\n /// @return assets_ deposited assets amount\n function mintWithSignature(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_,\n IAllowanceTransfer.PermitSingle calldata permit_,\n bytes calldata signature_\n ) external returns (uint256 assets_);\n}\n\ninterface IFTokenNativeUnderlying is IFToken {\n /// @notice address that is mapped to the chain native token at Liquidity\n function NATIVE_TOKEN_ADDRESS() external view returns (address);\n\n /// @notice deposits `msg.value` amount of native token for `receiver_`.\n /// `msg.value` must be at least `minDeposit()` amount; reverts otherwise.\n /// Recommended to use `depositNative()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return shares_ actually minted shares\n function depositNative(address receiver_) external payable returns (uint256 shares_);\n\n /// @notice same as {depositNative} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of shares is not reached\n function depositNative(address receiver_, uint256 minAmountOut_) external payable returns (uint256 shares_);\n\n /// @notice mints `shares_` for `receiver_`, paying with underlying native token.\n /// `shares_` must at least be `minMint()` amount; reverts otherwise.\n /// `shares_` set to type(uint256).max not supported.\n /// Note there might be tiny inaccuracies between requested `shares_` and actually received shares amount.\n /// Recommended to use `depositNative()` over mint because it is more gas efficient and less likely to revert.\n /// Recommended to use `mintNative()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return assets_ deposited assets amount\n function mintNative(uint256 shares_, address receiver_) external payable returns (uint256 assets_);\n\n /// @notice same as {mintNative} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MaxAmount()` if `maxAssets_` of assets is surpassed to mint `shares_`.\n function mintNative(\n uint256 shares_,\n address receiver_,\n uint256 maxAssets_\n ) external payable returns (uint256 assets_);\n\n /// @notice withdraws `assets_` amount in native underlying to `receiver_`, burning shares of `owner_`.\n /// If `assets_` equals uint256.max then the whole fToken balance of `owner_` is withdrawn.This does not\n /// consider withdrawal limit at liquidity so best to check with `maxWithdraw()` before.\n /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.\n /// Recommended to use `withdrawNative()` with a `maxSharesBurn_` param instead to set acceptable limit.\n /// @return shares_ burned shares\n function withdrawNative(uint256 assets_, address receiver_, address owner_) external returns (uint256 shares_);\n\n /// @notice same as {withdrawNative} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MaxAmount()` if `maxSharesBurn_` of shares burned is surpassed.\n function withdrawNative(\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_\n ) external returns (uint256 shares_);\n\n /// @notice redeems `shares_` to native underlying to `receiver_`, burning shares of `owner_`.\n /// If `shares_` equals uint256.max then the whole balance of `owner_` is withdrawn.This does not\n /// consider withdrawal limit at liquidity so best to check with `maxRedeem()` before.\n /// Recommended to use `withdrawNative()` over redeem because it is more gas efficient and can set specific amount.\n /// Recommended to use `redeemNative()` with a `minAmountOut_` param instead to set acceptable limit.\n /// @return assets_ withdrawn assets amount\n function redeemNative(uint256 shares_, address receiver_, address owner_) external returns (uint256 assets_);\n\n /// @notice same as {redeemNative} but with an additional setting for minimum output amount.\n /// reverts with `fToken__MinAmountOut()` if `minAmountOut_` of assets is not reached.\n function redeemNative(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_\n ) external returns (uint256 assets_);\n\n /// @notice withdraw amount of `assets_` in native token with ERC-2612 permit signature for fToken approval.\n /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.\n /// Note there might be tiny inaccuracies between requested `assets_` and actually received assets amount.\n /// allowance via signature should cover `previewWithdraw(assets_)` plus a little buffer to avoid revert.\n /// Inherent trust assumption that `msg.sender` will set `receiver_` and `minAmountOut_` as `owner_` intends\n /// (which is always the case when giving allowance to some spender).\n /// @param sharesToPermit_ shares amount to use for EIP2612 permit(). Should cover `previewWithdraw(assets_)` + small buffer.\n /// @param assets_ amount of assets to withdraw\n /// @param receiver_ receiver of withdrawn assets\n /// @param owner_ owner to withdraw from (must be signature signer)\n /// @param maxSharesBurn_ maximum accepted amount of shares burned\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for ERC-2612 permit\n /// @return shares_ burned shares amount\n function withdrawWithSignatureNative(\n uint256 sharesToPermit_,\n uint256 assets_,\n address receiver_,\n address owner_,\n uint256 maxSharesBurn_,\n uint256 deadline_,\n bytes calldata signature_\n ) external returns (uint256 shares_);\n\n /// @notice redeem amount of `shares_` as native token with ERC-2612 permit signature for fToken approval.\n /// `owner_` signs ERC-2612 permit `signature_` to give allowance of fTokens to `msg.sender`.\n /// Note there might be tiny inaccuracies between requested `shares_` to redeem and actually burned shares.\n /// allowance via signature must cover `shares_` plus a tiny buffer.\n /// Inherent trust assumption that `msg.sender` will set `receiver_` and `minAmountOut_` as `owner_` intends\n /// (which is always the case when giving allowance to some spender).\n /// Recommended to use `withdrawNative()` over redeem because it is more gas efficient and can set specific amount.\n /// @param shares_ amount of shares to redeem\n /// @param receiver_ receiver of withdrawn assets\n /// @param owner_ owner to withdraw from (must be signature signer)\n /// @param minAmountOut_ minimum accepted amount of assets withdrawn\n /// @param deadline_ deadline for signature validity\n /// @param signature_ packed signature of signing the EIP712 hash for ERC-2612 permit\n /// @return assets_ withdrawn assets amount\n function redeemWithSignatureNative(\n uint256 shares_,\n address receiver_,\n address owner_,\n uint256 minAmountOut_,\n uint256 deadline_,\n bytes calldata signature_\n ) external returns (uint256 assets_);\n}\n"
|
||
},
|
||
"contracts/protocols/lending/interfaces/iLendingFactory.sol": {
|
||
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IFluidLiquidity } from \"../../../liquidity/interfaces/iLiquidity.sol\";\n\ninterface IFluidLendingFactoryAdmin {\n /// @notice reads if a certain `auth_` address is an allowed auth or not. Owner is auth by default.\n function isAuth(address auth_) external view returns (bool);\n\n /// @notice Sets an address as allowed auth or not. Only callable by owner.\n /// @param auth_ address to set auth value for\n /// @param allowed_ bool flag for whether address is allowed as auth or not\n function setAuth(address auth_, bool allowed_) external;\n\n /// @notice reads if a certain `deployer_` address is an allowed deployer or not. Owner is deployer by default.\n function isDeployer(address deployer_) external view returns (bool);\n\n /// @notice Sets an address as allowed deployer or not. Only callable by owner.\n /// @param deployer_ address to set deployer value for\n /// @param allowed_ bool flag for whether address is allowed as deployer or not\n function setDeployer(address deployer_, bool allowed_) external;\n\n /// @notice Sets the `creationCode_` bytecode for a certain `fTokenType_`. Only callable by auths.\n /// @param fTokenType_ the fToken Type used to refer the creation code\n /// @param creationCode_ contract creation code. can be set to bytes(0) to remove a previously available `fTokenType_`\n function setFTokenCreationCode(string memory fTokenType_, bytes calldata creationCode_) external;\n\n /// @notice creates token for `asset_` for a lending protocol with interest. Only callable by deployers.\n /// @param asset_ address of the asset\n /// @param fTokenType_ type of fToken:\n /// - if it's the native token, it should use `NativeUnderlying`\n /// - otherwise it should use `fToken`\n /// - could be more types available, check `fTokenTypes()`\n /// @param isNativeUnderlying_ flag to signal fToken type that uses native underlying at Liquidity\n /// @return token_ address of the created token\n function createToken(\n address asset_,\n string calldata fTokenType_,\n bool isNativeUnderlying_\n ) external returns (address token_);\n}\n\ninterface IFluidLendingFactory is IFluidLendingFactoryAdmin {\n /// @notice list of all created tokens\n function allTokens() external view returns (address[] memory);\n\n /// @notice list of all fToken types that can be deployed\n function fTokenTypes() external view returns (string[] memory);\n\n /// @notice returns the creation code for a certain `fTokenType_`\n function fTokenCreationCode(string memory fTokenType_) external view returns (bytes memory);\n\n /// @notice address of the Liquidity contract.\n function LIQUIDITY() external view returns (IFluidLiquidity);\n\n /// @notice computes deterministic token address for `asset_` for a lending protocol\n /// @param asset_ address of the asset\n /// @param fTokenType_ type of fToken:\n /// - if it's the native token, it should use `NativeUnderlying`\n /// - otherwise it should use `fToken`\n /// - could be more types available, check `fTokenTypes()`\n /// @return token_ detemrinistic address of the computed token\n function computeToken(address asset_, string calldata fTokenType_) external view returns (address token_);\n}\n"
|
||
},
|
||
"contracts/protocols/lending/interfaces/iLendingRewardsRateModel.sol": {
|
||
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidLendingRewardsRateModel {\n /// @notice Calculates the current rewards rate (APR)\n /// @param totalAssets_ amount of assets in the lending\n /// @return rate_ rewards rate percentage per year with 1e12 RATE_PRECISION, e.g. 1e12 = 1%, 1e14 = 100%\n /// @return ended_ flag to signal that rewards have ended (always 0 going forward)\n /// @return startTime_ start time of rewards to compare against last update timestamp\n function getRate(uint256 totalAssets_) external view returns (uint256 rate_, bool ended_, uint256 startTime_);\n\n /// @notice Returns config constants for rewards rate model\n function getConfig()\n external\n view\n returns (\n uint256 duration_,\n uint256 startTime_,\n uint256 endTime_,\n uint256 startTvl_,\n uint256 maxRate_,\n uint256 rewardAmount_,\n address initiator_\n );\n}\n"
|
||
},
|
||
"contracts/protocols/lending/interfaces/permit2/iAllowanceTransfer.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\n/// @title AllowanceTransfer\n/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts\n/// @dev Requires user's token approval on the Permit2 contract\n/// from https://github.com/Uniswap/permit2/blob/main/src/interfaces/ISignatureTransfer.sol.\n/// Copyright (c) 2022 Uniswap Labs\ninterface IAllowanceTransfer {\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n /// @notice Thrown when an allowance on a token has expired.\n /// @param deadline The timestamp at which the allowed amount is no longer valid\n error AllowanceExpired(uint256 deadline);\n\n /// @notice Thrown when an allowance on a token has been depleted.\n /// @param amount The maximum amount allowed\n error InsufficientAllowance(uint256 amount);\n\n /// @notice Thrown when too many nonces are invalidated.\n error ExcessiveInvalidation();\n\n /// @notice Emits an event when the owner successfully invalidates an ordered nonce.\n event NonceInvalidation(\n address indexed owner,\n address indexed token,\n address indexed spender,\n uint48 newNonce,\n uint48 oldNonce\n );\n\n /// @notice Emits an event when the owner successfully sets permissions on a token for the spender.\n event Approval(\n address indexed owner,\n address indexed token,\n address indexed spender,\n uint160 amount,\n uint48 expiration\n );\n\n /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.\n event Permit(\n address indexed owner,\n address indexed token,\n address indexed spender,\n uint160 amount,\n uint48 expiration,\n uint48 nonce\n );\n\n /// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.\n event Lockdown(address indexed owner, address token, address spender);\n\n /// @notice The permit data for a token\n struct PermitDetails {\n // ERC20 token address\n address token;\n // the maximum amount allowed to spend\n uint160 amount;\n // timestamp at which a spender's token allowances become invalid\n uint48 expiration;\n // an incrementing value indexed per owner,token,and spender for each signature\n uint48 nonce;\n }\n\n /// @notice The permit message signed for a single token allownce\n struct PermitSingle {\n // the permit data for a single token alownce\n PermitDetails details;\n // address permissioned on the allowed tokens\n address spender;\n // deadline on the permit signature\n uint256 sigDeadline;\n }\n\n /// @notice The permit message signed for multiple token allowances\n struct PermitBatch {\n // the permit data for multiple token allowances\n PermitDetails[] details;\n // address permissioned on the allowed tokens\n address spender;\n // deadline on the permit signature\n uint256 sigDeadline;\n }\n\n /// @notice The saved permissions\n /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message\n /// @dev Setting amount to type(uint160).max sets an unlimited approval\n struct PackedAllowance {\n // amount allowed\n uint160 amount;\n // permission expiry\n uint48 expiration;\n // an incrementing value indexed per owner,token,and spender for each signature\n uint48 nonce;\n }\n\n /// @notice A token spender pair.\n struct TokenSpenderPair {\n // the token the spender is approved\n address token;\n // the spender address\n address spender;\n }\n\n /// @notice Details for a token transfer.\n struct AllowanceTransferDetails {\n // the owner of the token\n address from;\n // the recipient of the token\n address to;\n // the amount of the token\n uint160 amount;\n // the token to be transferred\n address token;\n }\n\n /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.\n /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]\n /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.\n function allowance(\n address user,\n address token,\n address spender\n ) external view returns (uint160 amount, uint48 expiration, uint48 nonce);\n\n /// @notice Approves the spender to use up to amount of the specified token up until the expiration\n /// @param token The token to approve\n /// @param spender The spender address to approve\n /// @param amount The approved amount of the token\n /// @param expiration The timestamp at which the approval is no longer valid\n /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve\n /// @dev Setting amount to type(uint160).max sets an unlimited approval\n function approve(address token, address spender, uint160 amount, uint48 expiration) external;\n\n /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature\n /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce\n /// @param owner The owner of the tokens being approved\n /// @param permitSingle Data signed over by the owner specifying the terms of approval\n /// @param signature The owner's signature over the permit data\n function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;\n\n /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature\n /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce\n /// @param owner The owner of the tokens being approved\n /// @param permitBatch Data signed over by the owner specifying the terms of approval\n /// @param signature The owner's signature over the permit data\n function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;\n\n /// @notice Transfer approved tokens from one address to another\n /// @param from The address to transfer from\n /// @param to The address of the recipient\n /// @param amount The amount of the token to transfer\n /// @param token The token address to transfer\n /// @dev Requires the from address to have approved at least the desired amount\n /// of tokens to msg.sender.\n function transferFrom(address from, address to, uint160 amount, address token) external;\n\n /// @notice Transfer approved tokens in a batch\n /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers\n /// @dev Requires the from addresses to have approved at least the desired amount\n /// of tokens to msg.sender.\n function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;\n\n /// @notice Enables performing a \"lockdown\" of the sender's Permit2 identity\n /// by batch revoking approvals\n /// @param approvals Array of approvals to revoke.\n function lockdown(TokenSpenderPair[] calldata approvals) external;\n\n /// @notice Invalidate nonces for a given (token, spender) pair\n /// @param token The token to invalidate nonces for\n /// @param spender The spender to invalidate nonces for\n /// @param newNonce The new nonce to set. Invalidates all nonces less than it.\n /// @dev Can't invalidate more than 2**16 nonces per transaction.\n function invalidateNonces(address token, address spender, uint48 newNonce) external;\n}\n"
|
||
},
|
||
"contracts/protocols/lending/lendingRewardsRateModel/main.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidLendingRewardsRateModel } from \"../interfaces/iLendingRewardsRateModel.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { Error } from \"../error.sol\";\n\n/// @title LendingRewardsRateModel\n/// @notice Calculates rewards rate used for an fToken based on a rewardAmount over a given duration.\n/// Rewards start once the allowed initiator address triggers `start()` and only accrue above a certain startTVL.\n/// Max rate cap is at 50%.\ncontract FluidLendingRewardsRateModel is IFluidLendingRewardsRateModel, Error {\n /// @notice Emitted when rewards are started\n event LogRewardsStarted(uint256 startTime, uint256 endTime);\n\n /// @dev precision decimals for rewards rate\n uint256 internal constant RATE_PRECISION = 1e12;\n\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n\n /// @dev maximum rewards rate is 50%. no config higher than this should be possible.\n uint256 internal constant MAX_RATE = 50 * RATE_PRECISION; // 1e12 = 1%, this is 50%.\n\n /// @dev tvl below which rewards rate is 0\n uint256 internal immutable START_TVL;\n\n /// @dev for how long rewards should run\n uint256 internal immutable DURATION;\n\n /// @dev annualized reward based on constructor input params (duration, rewardAmount)\n uint256 internal immutable YEARLY_REWARD;\n\n /// @dev total amounts to be distributed. not needed but stored for easier tracking via `getConfig`\n uint256 internal immutable REWARD_AMOUNT;\n\n /// @dev address which has access to call start() which kickstarts the rewards\n address internal immutable INITIATOR;\n\n /// @dev address of the previously active lending rewards rate model for smooth transition. Can be zero address if none.\n IFluidLendingRewardsRateModel public immutable PREVIOUS_MODEL;\n /// @dev end time of previous lending rewards rate model. 0 if there is no previous model.\n uint256 internal immutable PREVIOUS_MODEL_END_TIME;\n\n /// @dev when rewards got started\n uint96 internal startTime;\n /// @dev when rewards will get over\n uint96 internal endTime;\n\n /// @dev Validates that an address is not the zero address\n modifier validAddress(address value_) {\n if (value_ == address(0)) {\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__ZeroAddress);\n }\n _;\n }\n\n /// @notice sets immutable vars for rewards rate config based on input params.\n /// @param duration_ for how long rewards should run\n /// @param startTvl_ tvl below which rate is 0\n /// @param rewardAmount_ total amount of underlying asset to be distributed as rewards\n /// @param initiator_ address which has access to kickstart the rewards, if previousModel is address zero\n /// @param previousModel_ address of previously active lendingRewardsRateModel. can be zero address if none.\n constructor(\n uint256 duration_,\n uint256 startTvl_,\n uint256 rewardAmount_,\n address initiator_,\n IFluidLendingRewardsRateModel previousModel_\n ) {\n // sanity checks\n if (\n duration_ == 0 ||\n rewardAmount_ == 0 ||\n startTvl_ == 0 ||\n (initiator_ == address(0) && address(previousModel_) == address(0))\n ) {\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__InvalidParams);\n }\n\n START_TVL = startTvl_;\n DURATION = duration_;\n REWARD_AMOUNT = rewardAmount_;\n INITIATOR = initiator_;\n\n YEARLY_REWARD = (rewardAmount_ * SECONDS_PER_YEAR) / DURATION;\n\n if (address(previousModel_) != address(0)) {\n PREVIOUS_MODEL = previousModel_;\n (, , PREVIOUS_MODEL_END_TIME, , , , ) = previousModel_.getConfig();\n if (PREVIOUS_MODEL_END_TIME == 0) {\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__InvalidParams);\n }\n\n // start current model exactly when previous model ends. no trigger via start() needed.\n startTime = uint96(PREVIOUS_MODEL_END_TIME);\n endTime = uint96(PREVIOUS_MODEL_END_TIME + DURATION);\n\n emit LogRewardsStarted(startTime, endTime);\n }\n }\n\n /// @inheritdoc IFluidLendingRewardsRateModel\n function getConfig()\n external\n view\n returns (\n uint256 duration_,\n uint256 startTime_,\n uint256 endTime_,\n uint256 startTvl_,\n uint256 maxRate_,\n uint256 rewardAmount_,\n address initiator_\n )\n {\n return (DURATION, startTime, endTime, START_TVL, MAX_RATE, REWARD_AMOUNT, INITIATOR);\n }\n\n /// @inheritdoc IFluidLendingRewardsRateModel\n function getRate(uint256 totalAssets_) public view returns (uint256 rate_, bool ended_, uint256 startTime_) {\n if (block.timestamp <= PREVIOUS_MODEL_END_TIME) {\n // return previous model reate until previous model end time.\n return PREVIOUS_MODEL.getRate(totalAssets_);\n }\n\n startTime_ = startTime;\n uint endTime_ = endTime;\n if (startTime_ == 0 || endTime_ == 0) {\n return (0, false, startTime_);\n }\n if (block.timestamp > endTime_) {\n return (0, true, startTime_);\n }\n if (totalAssets_ < START_TVL) {\n return (0, false, startTime_);\n }\n\n rate_ = (YEARLY_REWARD * 1e14) / totalAssets_;\n\n return (rate_ > MAX_RATE ? MAX_RATE : rate_, false, startTime_);\n }\n\n function start() external {\n if (msg.sender != INITIATOR) {\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__NotTheInitiator);\n }\n if (startTime > 0 || endTime > 0) {\n // will fail if started in constructor for smooth transition from previous model\n revert FluidLendingError(ErrorTypes.LendingRewardsRateModel__AlreadyStarted);\n }\n startTime = uint96(block.timestamp);\n endTime = uint96(block.timestamp + DURATION);\n\n emit LogRewardsStarted(startTime, endTime);\n }\n}\n"
|
||
},
|
||
"contracts/protocols/lending/stakingRewards/main.sol": {
|
||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ReentrancyGuard } from \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport { IERC20Permit } from \"@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\nimport { Owned } from \"solmate/src/auth/Owned.sol\";\n\n// Adapted from https://github.com/Uniswap/liquidity-staker/blob/master/contracts/StakingRewards.sol\ncontract FluidLendingStakingRewards is Owned, ReentrancyGuard {\n using SafeERC20 for IERC20;\n\n IERC20 public immutable rewardsToken; // should be INST or any ERC20\n IERC20 public immutable stakingToken; // should be fToken\n\n /* ========== STATE VARIABLES ========== */\n\n // Owned and ReentranyGuard storage variables before\n\n uint40 internal _periodFinish;\n uint40 public lastUpdateTime;\n uint40 internal _rewardsDuration; // e.g. 60 days\n uint136 internal _rewardRate;\n\n // ------------------------------ next slot\n\n uint128 public rewardPerTokenStored;\n uint128 internal _totalSupply;\n\n // ------------------------------ next slot\n\n uint40 public nextRewardsDuration; // e.g. 60 days\n uint216 public nextRewards;\n\n // ------------------------------ next slot\n\n mapping(address => uint256) public userRewardPerTokenPaid;\n mapping(address => uint256) public rewards;\n\n mapping(address => uint256) internal _balances;\n\n /* ========== CONSTRUCTOR ========== */\n\n constructor(address owner_, IERC20 rewardsToken_, IERC20 stakingToken_, uint40 rewardsDuration_) Owned(owner_) {\n require(address(rewardsToken_) != address(0), \"Invalid params\");\n require(address(stakingToken_) != address(0), \"Invalid params\");\n require(owner_ != address(0), \"Invalid params\"); // Owned does not have a zero check for owner_\n require(rewardsDuration_ > 0, \"Invalid params\");\n\n rewardsToken = rewardsToken_;\n stakingToken = stakingToken_;\n _rewardsDuration = rewardsDuration_;\n }\n\n /* ========== VIEWS ========== */\n\n function nextPeriodFinish() public view returns (uint256) {\n if (nextRewardsDuration == 0) {\n return 0;\n }\n return _periodFinish + nextRewardsDuration;\n }\n\n function nextRewardRate() public view returns (uint256) {\n if (nextRewards == 0) {\n return 0;\n }\n return nextRewards / nextRewardsDuration;\n }\n\n function periodFinish() public view returns (uint256) {\n if (block.timestamp <= _periodFinish || nextRewardsDuration == 0) {\n return _periodFinish;\n }\n return nextPeriodFinish();\n }\n\n function rewardRate() public view returns (uint256) {\n if (block.timestamp <= _periodFinish || nextRewardsDuration == 0) {\n return _rewardRate;\n }\n return nextRewardRate();\n }\n\n function rewardsDuration() public view returns (uint256) {\n if (block.timestamp <= _periodFinish || nextRewardsDuration == 0) {\n return _rewardsDuration;\n }\n return nextRewardsDuration;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n /// @notice gets last time where rewards accrue, also considering already queued next rewards\n function lastTimeRewardApplicable() public view returns (uint256) {\n if (block.timestamp <= _periodFinish) {\n return block.timestamp;\n }\n\n if (nextRewardsDuration == 0) {\n return _periodFinish;\n }\n\n // check if block.timestamp is within next rewards duration\n uint256 nextPeriodFinish_ = nextPeriodFinish();\n if (block.timestamp <= nextPeriodFinish_) {\n return block.timestamp;\n }\n return nextPeriodFinish_;\n }\n\n /// @notice gets reward amount per token, also considering automatic transition to queued next rewards\n function rewardPerToken() public view returns (uint256 rewardPerToken_) {\n if (_totalSupply == 0) {\n return rewardPerTokenStored;\n }\n\n // reward per token for current rewards\n // get lastTimeRewardApplicable for storage vars (without next queued rewards as returned by view methods)\n uint256 lastTimeRewardApplicable_ = block.timestamp < _periodFinish ? block.timestamp : _periodFinish;\n\n rewardPerToken_ = (((lastTimeRewardApplicable_ - lastUpdateTime) * _rewardRate * 1e18) / _totalSupply);\n\n if (block.timestamp > _periodFinish && nextRewardsDuration > 0) {\n // previous rewards ended and next rewards queued, take into account accrued rewards:\n\n // check if block.timestamp is within next rewards duration\n rewardPerToken_ += (((lastTimeRewardApplicable() - _periodFinish) * nextRewardRate() * 1e18) /\n _totalSupply);\n }\n\n rewardPerToken_ += rewardPerTokenStored;\n }\n\n /// @notice gets earned reward amount for an `account`, also considering automatic transition to queued next rewards\n function earned(address account) public view returns (uint256) {\n return (_balances[account] * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18 + rewards[account];\n }\n\n /// @notice gets reward amount for current duration, also considering automatic transition to queued next rewards\n function getRewardForDuration() public view returns (uint256) {\n return rewardRate() * rewardsDuration();\n }\n\n /* ========== MUTATIVE FUNCTIONS ========== */\n\n function stakeWithPermit(\n uint256 amount,\n uint deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external nonReentrant updateReward(msg.sender) {\n require(amount > 0, \"Cannot stake 0\");\n\n // permit\n IERC20Permit(address(stakingToken)).permit(msg.sender, address(this), amount, deadline, v, r, s);\n\n stakingToken.safeTransferFrom(msg.sender, address(this), amount);\n\n _totalSupply = _totalSupply + uint128(amount);\n _balances[msg.sender] = _balances[msg.sender] + amount;\n\n emit Staked(msg.sender, amount);\n }\n\n function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {\n require(amount > 0, \"Cannot stake 0\");\n\n stakingToken.safeTransferFrom(msg.sender, address(this), amount);\n\n _totalSupply = _totalSupply + uint128(amount);\n _balances[msg.sender] = _balances[msg.sender] + amount;\n\n emit Staked(msg.sender, amount);\n }\n\n function withdraw(uint256 amount) public nonReentrant updateReward(msg.sender) {\n require(amount > 0, \"Cannot withdraw 0\");\n\n _totalSupply = _totalSupply - uint128(amount);\n _balances[msg.sender] = _balances[msg.sender] - amount;\n\n stakingToken.safeTransfer(msg.sender, amount);\n\n emit Withdrawn(msg.sender, amount);\n }\n\n function getReward() public nonReentrant updateReward(msg.sender) {\n uint256 reward = rewards[msg.sender];\n if (reward > 0) {\n rewards[msg.sender] = 0;\n rewardsToken.safeTransfer(msg.sender, reward);\n emit RewardPaid(msg.sender, reward);\n }\n }\n\n function exit() external {\n withdraw(_balances[msg.sender]);\n getReward();\n }\n\n /// @notice updates rewards until current block.timestamp or `periodFinish`. Transitions to next rewards\n /// if previous rewards ended and next ones were queued.\n function updateRewards() public nonReentrant updateReward(address(0)) {}\n\n /* ========== RESTRICTED FUNCTIONS ========== */\n\n /// @notice queues next rewards that will be transitioned to automatically after current rewards reach `periodFinish`.\n function queueNextRewardAmount(uint216 nextReward_, uint40 nextDuration_) external onlyOwner {\n require(block.timestamp < _periodFinish, \"Previous duration already ended\");\n require(nextDuration_ > 0 && nextReward_ > 0, \"Invalid params\");\n // must not already be queued\n require(nextRewardsDuration == 0, \"Already queued next rewards\");\n\n // Ensure the provided reward amount is not more than the balance in the contract.\n // This keeps the reward rate in the right range, preventing overflows due to\n // very high values of rewardRate in the earned and rewardsPerToken functions;\n // Reward + nextReward must be less than 2^256 / 10^18 to avoid overflow.\n uint256 balance_ = rewardsToken.balanceOf(address(this));\n uint256 remainingCurrentReward_ = (_periodFinish - block.timestamp) * _rewardRate;\n uint256 requiredBalance_ = remainingCurrentReward_ + nextReward_;\n require(requiredBalance_ <= balance_, \"Provided reward too high\");\n\n nextRewards = nextReward_;\n nextRewardsDuration = nextDuration_;\n\n emit NextRewardQueued(nextReward_, nextDuration_);\n }\n\n /// @notice add new rewards and update reward duration AFTER a reward period has ended.\n function notifyRewardAmountWithDuration(\n uint256 reward_,\n uint40 newDuration_\n ) external onlyOwner updateReward(address(0)) {\n require(block.timestamp > _periodFinish, \"Previous duration not ended\");\n // @dev nextRewardsDuration == 0 should not be needed because if there are next rewards queued and block.timestamp > periodFinish,\n // then updateReward(address(0)) would automatically transition to the queued rewards and set nextRewardsDuration == 0.\n // Adding the check here anyway to be safe as gas optimization on an Admin Method is not necessary.\n require(nextRewardsDuration == 0, \"Already queued next rewards\");\n require(newDuration_ > 0, \"Invalid params\");\n\n _rewardsDuration = newDuration_;\n notifyRewardAmount(reward_);\n }\n\n /// @notice add new rewards or top-up adding to current rewards, adjusting rewardRate going forward for leftover + newReward\n /// until block.timestamp + duration\n function notifyRewardAmount(uint256 reward_) public onlyOwner updateReward(address(0)) {\n require(nextRewardsDuration == 0, \"Already queued next rewards\");\n\n if (block.timestamp >= _periodFinish) {\n _rewardRate = uint136(reward_ / _rewardsDuration);\n } else {\n uint256 remaining_ = _periodFinish - block.timestamp;\n uint256 leftover_ = remaining_ * _rewardRate;\n _rewardRate = uint136((reward_ + leftover_) / _rewardsDuration);\n }\n\n // Ensure the provided reward amount is not more than the balance in the contract.\n // This keeps the reward rate in the right range, preventing overflows due to\n // very high values of rewardRate in the earned and rewardsPerToken functions;\n // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.\n uint256 balance_ = rewardsToken.balanceOf(address(this));\n require(_rewardRate <= balance_ / _rewardsDuration, \"Provided reward too high\");\n\n lastUpdateTime = uint40(block.timestamp);\n _periodFinish = uint40(block.timestamp + _rewardsDuration);\n emit RewardAdded(reward_);\n }\n\n /// @notice Spell allows owner aka governance to do any arbitrary call\n /// @param target_ Address to which the call needs to be delegated\n /// @param data_ Data to execute at the delegated address\n function spell(address target_, bytes memory data_) external onlyOwner returns (bytes memory response_) {\n assembly {\n let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)\n let size := returndatasize()\n\n response_ := mload(0x40)\n mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))\n mstore(response_, size)\n returndatacopy(add(response_, 0x20), 0, size)\n\n switch iszero(succeeded)\n case 1 {\n // throw if delegatecall failed\n returndatacopy(0x00, 0x00, size)\n revert(0x00, size)\n }\n }\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier updateReward(address account) {\n // get lastTimeRewardApplicable for storage vars (without next queued rewards as returned by view methods)\n uint256 lastTimeRewardApplicable_ = block.timestamp < _periodFinish ? block.timestamp : _periodFinish;\n\n // get reward per Token for storage vars (without next queued rewards as returned by view methods)\n uint256 rewardPerToken_;\n if (_totalSupply > 0) {\n rewardPerToken_ =\n rewardPerTokenStored +\n (((lastTimeRewardApplicable_ - lastUpdateTime) * _rewardRate * 1e18) / _totalSupply);\n }\n\n rewardPerTokenStored = uint128(rewardPerToken_);\n lastUpdateTime = uint40(lastTimeRewardApplicable_);\n\n if (block.timestamp > _periodFinish && nextRewardsDuration > 0) {\n // previous rewards ended, and new rewards were queued -> start new rewards.\n\n // previous rewards fully distributed until `periodFinish` by updating `rewardPerTokenStored`\n // according to `rewardRate` that was valid until period finish, with calls above.\n\n // transition to new rewards\n _rewardRate = uint136(nextRewards / nextRewardsDuration);\n _rewardsDuration = nextRewardsDuration;\n\n // new rewards started right at periodFinish\n _periodFinish = uint40(_periodFinish + _rewardsDuration);\n\n emit RewardAdded(nextRewards);\n\n // reset next rewards storage vars\n nextRewards = 0;\n nextRewardsDuration = 0;\n\n // update rewardPerTokenStored again to go until current block.timestamp now\n // can use normal view methods here as next rewards storage vars have just been set to 0\n rewardPerTokenStored = uint128(rewardPerToken());\n lastUpdateTime = uint40(lastTimeRewardApplicable());\n }\n\n if (account != address(0)) {\n rewards[account] = earned(account);\n userRewardPerTokenPaid[account] = rewardPerTokenStored;\n }\n _;\n }\n\n /* ========== EVENTS ========== */\n\n event RewardAdded(uint256 reward);\n event Staked(address indexed user, uint256 amount);\n event Withdrawn(address indexed user, uint256 amount);\n event RewardPaid(address indexed user, uint256 reward);\n event NextRewardQueued(uint256 reward, uint256 duration);\n}\n"
|
||
},
|
||
"contracts/protocols/vault/error.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Error {\n error FluidVaultError(uint256 errorId_);\n\n /// @notice used to simulate liquidation to find the maximum liquidatable amounts\n error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated);\n}\n"
|
||
},
|
||
"contracts/protocols/vault/errorTypes.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | Vault Factory | \n |__________________________________*/\n\n uint256 internal constant VaultFactory__InvalidOperation = 30001;\n uint256 internal constant VaultFactory__Unauthorized = 30002;\n uint256 internal constant VaultFactory__SameTokenNotAllowed = 30003;\n uint256 internal constant VaultFactory__InvalidParams = 30004;\n uint256 internal constant VaultFactory__InvalidVault = 30005;\n uint256 internal constant VaultFactory__InvalidVaultAddress = 30006;\n uint256 internal constant VaultFactory__OnlyDelegateCallAllowed = 30007;\n\n /***********************************|\n | VaultT1 | \n |__________________________________*/\n\n /// @notice thrown at reentrancy\n uint256 internal constant VaultT1__AlreadyEntered = 31001;\n\n /// @notice thrown when user sends deposit & borrow amount as 0\n uint256 internal constant VaultT1__InvalidOperateAmount = 31002;\n\n /// @notice thrown when msg.value is not in sync with native token deposit or payback\n uint256 internal constant VaultT1__InvalidMsgValueOperate = 31003;\n\n /// @notice thrown when msg.sender is not the owner of the vault\n uint256 internal constant VaultT1__NotAnOwner = 31004;\n\n /// @notice thrown when user's position does not exist. Sending the wrong index from the frontend\n uint256 internal constant VaultT1__TickIsEmpty = 31005;\n\n /// @notice thrown when the user's position is above CF and the user tries to make it more risky by trying to withdraw or borrow\n uint256 internal constant VaultT1__PositionAboveCF = 31006;\n\n /// @notice thrown when the top tick is not initialized. Happens if the vault is totally new or all the user's left\n uint256 internal constant VaultT1__TopTickDoesNotExist = 31007;\n\n /// @notice thrown when msg.value in liquidate is not in sync payback\n uint256 internal constant VaultT1__InvalidMsgValueLiquidate = 31008;\n\n /// @notice thrown when slippage is more on liquidation than what the liquidator sent\n uint256 internal constant VaultT1__ExcessSlippageLiquidation = 31009;\n\n /// @notice thrown when msg.sender is not the rebalancer/reserve contract\n uint256 internal constant VaultT1__NotRebalancer = 31010;\n\n /// @notice thrown when NFT of one vault interacts with the NFT of other vault\n uint256 internal constant VaultT1__NftNotOfThisVault = 31011;\n\n /// @notice thrown when the token is not initialized on the liquidity contract\n uint256 internal constant VaultT1__TokenNotInitialized = 31012;\n\n /// @notice thrown when admin updates fallback if a non-auth calls vault\n uint256 internal constant VaultT1__NotAnAuth = 31013;\n\n /// @notice thrown in operate when user tries to witdhraw more collateral than deposited\n uint256 internal constant VaultT1__ExcessCollateralWithdrawal = 31014;\n\n /// @notice thrown in operate when user tries to payback more debt than borrowed\n uint256 internal constant VaultT1__ExcessDebtPayback = 31015;\n\n /// @notice thrown when user try to withdrawal more than operate's withdrawal limit\n uint256 internal constant VaultT1__WithdrawMoreThanOperateLimit = 31016;\n\n /// @notice thrown when caller of liquidityCallback is not Liquidity\n uint256 internal constant VaultT1__InvalidLiquidityCallbackAddress = 31017;\n\n /// @notice thrown when reentrancy is not already on\n uint256 internal constant VaultT1__NotEntered = 31018;\n\n /// @notice thrown when someone directly calls secondary implementation contract\n uint256 internal constant VaultT1__OnlyDelegateCallAllowed = 31019;\n\n /// @notice thrown when the safeTransferFrom for a token amount failed\n uint256 internal constant VaultT1__TransferFromFailed = 31020;\n\n /// @notice thrown when exchange price overflows while updating on storage\n uint256 internal constant VaultT1__ExchangePriceOverFlow = 31021;\n\n /// @notice thrown when debt to liquidate amt is sent wrong\n uint256 internal constant VaultT1__InvalidLiquidationAmt = 31022;\n\n /// @notice thrown when user debt or collateral goes above 2**128 or below -2**128\n uint256 internal constant VaultT1__UserCollateralDebtExceed = 31023;\n\n /// @notice thrown if on liquidation branch debt becomes lower than 100\n uint256 internal constant VaultT1__BranchDebtTooLow = 31024;\n\n /// @notice thrown when tick's debt is less than 10000\n uint256 internal constant VaultT1__TickDebtTooLow = 31025;\n\n /// @notice thrown when the received new liquidity exchange price is of unexpected value (< than the old one)\n uint256 internal constant VaultT1__LiquidityExchangePriceUnexpected = 31026;\n\n /// @notice thrown when user's debt is less than 10000\n uint256 internal constant VaultT1__UserDebtTooLow = 31027;\n\n /// @notice thrown when on only payback and only deposit the ratio of position increases\n uint256 internal constant VaultT1__InvalidPaybackOrDeposit = 31028;\n\n /// @notice thrown when liquidation just happens of a single partial or when there's nothing to liquidate\n uint256 internal constant VaultT1__InvalidLiquidation = 31029;\n\n /// @notice thrown when msg.value is sent wrong in rebalance\n uint256 internal constant VaultT1__InvalidMsgValueInRebalance = 31030;\n\n /// @notice thrown when nothing rebalanced\n uint256 internal constant VaultT1__NothingToRebalance = 31031;\n\n /// @notice thrown on unforseen liquidation scenarios. Might never come in use.\n uint256 internal constant VaultT1__LiquidationReverts = 31032;\n\n /// @notice thrown when oracle price is > 1e54\n uint256 internal constant VaultT1__InvalidOraclePrice = 31033;\n\n /***********************************|\n | ERC721 | \n |__________________________________*/\n\n uint256 internal constant ERC721__InvalidParams = 32001;\n uint256 internal constant ERC721__Unauthorized = 32002;\n uint256 internal constant ERC721__InvalidOperation = 32003;\n uint256 internal constant ERC721__UnsafeRecipient = 32004;\n uint256 internal constant ERC721__OutOfBoundsIndex = 32005;\n\n /***********************************|\n | Vault Admin | \n |__________________________________*/\n\n /// @notice thrown when admin tries to setup invalid value which are crossing limits\n uint256 internal constant VaultT1Admin__ValueAboveLimit = 33001;\n\n /// @notice when someone directly calls admin implementation contract\n uint256 internal constant VaultT1Admin__OnlyDelegateCallAllowed = 33002;\n\n /// @notice thrown when auth sends NFT ID as 0 while collecting dust debt\n uint256 internal constant VaultT1Admin__NftIdShouldBeNonZero = 33003;\n\n /// @notice thrown when trying to collect dust debt of NFT which is not of this vault\n uint256 internal constant VaultT1Admin__NftNotOfThisVault = 33004;\n\n /// @notice thrown when dust debt of NFT is 0, meaning nothing to collect\n uint256 internal constant VaultT1Admin__DustDebtIsZero = 33005;\n\n /// @notice thrown when final debt after liquidation is not 0, meaning position 100% liquidated\n uint256 internal constant VaultT1Admin__FinalDebtShouldBeZero = 33006;\n\n /// @notice thrown when NFT is not liquidated state\n uint256 internal constant VaultT1Admin__NftNotLiquidated = 33007;\n\n /// @notice thrown when total absorbed dust debt is 0\n uint256 internal constant VaultT1Admin__AbsorbedDustDebtIsZero = 33008;\n\n /// @notice thrown when address is set as 0\n uint256 internal constant VaultT1Admin__AddressZeroNotAllowed = 33009;\n\n /***********************************|\n | Vault Rewards | \n |__________________________________*/\n\n uint256 internal constant VaultRewards__Unauthorized = 34001;\n uint256 internal constant VaultRewards__AddressZero = 34002;\n uint256 internal constant VaultRewards__InvalidParams = 34003;\n uint256 internal constant VaultRewards__NewMagnifierSameAsOldMagnifier = 34004;\n uint256 internal constant VaultRewards__NotTheInitiator = 34005;\n uint256 internal constant VaultRewards__AlreadyStarted = 34006;\n uint256 internal constant VaultRewards__RewardsNotStartedOrEnded = 34007;\n}\n"
|
||
},
|
||
"contracts/protocols/vault/factory/deploymentLogics/vaultT1Logic.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { SSTORE2 } from \"solmate/src/utils/SSTORE2.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { Error } from \"../../error.sol\";\nimport { IFluidVaultFactory } from \"../../interfaces/iVaultFactory.sol\";\n\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\n\nimport { IFluidVaultT1 } from \"../../interfaces/iVaultT1.sol\";\nimport { FluidVaultT1 } from \"../../vaultT1/coreModule/main.sol\";\n\ninterface IERC20 {\n function decimals() external view returns (uint8);\n}\n\ncontract FluidVaultT1DeploymentLogic is Error {\n address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @dev SSTORE2 pointer for the VaultT1 creation code. Stored externally to reduce factory bytecode (in 2 parts)\n address internal immutable VAULT_T1_CREATIONCODE_ADDRESS_1;\n address internal immutable VAULT_T1_CREATIONCODE_ADDRESS_2;\n\n /// @notice address of liquidity contract\n address public immutable LIQUIDITY;\n\n /// @notice address of Admin implementation\n address public immutable ADMIN_IMPLEMENTATION;\n\n /// @notice address of Secondary implementation\n address public immutable SECONDARY_IMPLEMENTATION;\n\n /// @notice address of this contract\n address public immutable ADDRESS_THIS;\n\n /// @notice Emitted when a new vaultT1 is deployed.\n /// @param vault The address of the newly deployed vault.\n /// @param vaultId The id of the newly deployed vault.\n /// @param supplyToken The address of the supply token.\n /// @param borrowToken The address of the borrow token.\n event VaultT1Deployed(\n address indexed vault,\n uint256 vaultId,\n address indexed supplyToken,\n address indexed borrowToken\n );\n\n constructor(address liquidity_, address vaultAdminImplementation_, address vaultSecondaryImplementation_) {\n LIQUIDITY = liquidity_;\n ADMIN_IMPLEMENTATION = vaultAdminImplementation_;\n SECONDARY_IMPLEMENTATION = vaultSecondaryImplementation_;\n\n // split storing creation code into two SSTORE2 pointers, because:\n // due to contract code limits 24576 bytes is the maximum amount of data that can be written in a single pointer / key.\n // Attempting to write more will result in failure.\n // So by splitting in two parts we can make sure that the contract bytecode size can use up the full limit of 24576 bytes.\n uint256 creationCodeLength_ = type(FluidVaultT1).creationCode.length;\n VAULT_T1_CREATIONCODE_ADDRESS_1 = SSTORE2.write(\n _bytesSlice(type(FluidVaultT1).creationCode, 0, creationCodeLength_ / 2)\n );\n // slice lengths:\n // when even length, e.g. 250:\n // part 1 = 0 -> 250 / 2, so 0 until 125 length, so 0 -> 125\n // part 2 = 250 / 2 -> 250 - 250 / 2, so 125 until 125 length, so 125 -> 250\n // when odd length: e.g. 251:\n // part 1 = 0 -> 251 / 2, so 0 until 125 length, so 0 -> 125\n // part 2 = 251 / 2 -> 251 - 251 / 2, so 125 until 126 length, so 125 -> 251\n VAULT_T1_CREATIONCODE_ADDRESS_2 = SSTORE2.write(\n _bytesSlice(\n type(FluidVaultT1).creationCode,\n creationCodeLength_ / 2,\n creationCodeLength_ - creationCodeLength_ / 2\n )\n );\n\n ADDRESS_THIS = address(this);\n }\n\n /// @notice Computes vaultT1 bytecode for the given supply token (`supplyToken_`) and borrow token (`borrowToken_`).\n /// This will be called by the VaultFactory via .delegateCall\n /// @param supplyToken_ The address of the supply token.\n /// @param borrowToken_ The address of the borrow token.\n /// @return vaultCreationBytecode_ Returns the bytecode of the new vault to deploy.\n function vaultT1(\n address supplyToken_,\n address borrowToken_\n ) external returns (bytes memory vaultCreationBytecode_) {\n if (address(this) == ADDRESS_THIS) revert FluidVaultError(ErrorTypes.VaultFactory__OnlyDelegateCallAllowed);\n\n if (supplyToken_ == borrowToken_) revert FluidVaultError(ErrorTypes.VaultFactory__SameTokenNotAllowed);\n\n IFluidVaultT1.ConstantViews memory constants_;\n constants_.liquidity = LIQUIDITY;\n constants_.factory = address(this);\n constants_.adminImplementation = ADMIN_IMPLEMENTATION;\n constants_.secondaryImplementation = SECONDARY_IMPLEMENTATION;\n constants_.supplyToken = supplyToken_;\n constants_.supplyDecimals = supplyToken_ != NATIVE_TOKEN ? IERC20(supplyToken_).decimals() : 18;\n constants_.borrowToken = borrowToken_;\n constants_.borrowDecimals = borrowToken_ != NATIVE_TOKEN ? IERC20(borrowToken_).decimals() : 18;\n constants_.vaultId = IFluidVaultFactory(address(this)).totalVaults();\n\n address vault_ = IFluidVaultFactory(address(this)).getVaultAddress(constants_.vaultId);\n\n constants_ = _calculateLiquidityVaultSlots(constants_, vault_);\n\n vaultCreationBytecode_ = abi.encodePacked(vaultT1CreationBytecode(), abi.encode(constants_));\n\n emit VaultT1Deployed(vault_, constants_.vaultId, supplyToken_, borrowToken_);\n\n return vaultCreationBytecode_;\n }\n\n /// @notice returns the stored VaultT1 creation bytecode\n function vaultT1CreationBytecode() public view returns (bytes memory) {\n return\n _bytesConcat(SSTORE2.read(VAULT_T1_CREATIONCODE_ADDRESS_1), SSTORE2.read(VAULT_T1_CREATIONCODE_ADDRESS_2));\n }\n\n /// @dev Calculates the liquidity vault slots for the given supply token, borrow token, and vault (`vault_`).\n /// @param constants_ Constants struct as used in Vault T1\n /// @param vault_ The address of the vault.\n /// @return liquidityVaultSlots_ Returns the calculated liquidity vault slots set in the `IFluidVaultT1.ConstantViews` struct.\n function _calculateLiquidityVaultSlots(\n IFluidVaultT1.ConstantViews memory constants_,\n address vault_\n ) private pure returns (IFluidVaultT1.ConstantViews memory) {\n constants_.liquiditySupplyExchangePriceSlot = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n constants_.supplyToken\n );\n constants_.liquidityBorrowExchangePriceSlot = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n constants_.borrowToken\n );\n constants_.liquidityUserSupplySlot = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT,\n vault_,\n constants_.supplyToken\n );\n constants_.liquidityUserBorrowSlot = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT,\n vault_,\n constants_.borrowToken\n );\n return constants_;\n }\n\n // @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\n function _bytesConcat(bytes memory _preBytes, bytes memory _postBytes) private pure returns (bytes memory) {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(\n 0x40,\n and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n )\n )\n }\n\n return tempBytes;\n }\n\n // @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\n function _bytesSlice(bytes memory _bytes, uint256 _start, uint256 _length) private pure returns (bytes memory) {\n require(_length + 31 >= _length, \"slice_overflow\");\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n}\n"
|
||
},
|
||
"contracts/protocols/vault/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/common/variables.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Variables {\n /***********************************|\n | Storage Variables |\n |__________________________________*/\n\n /// note: in all variables. For tick >= 0 are represented with bit as 1, tick < 0 are represented with bit as 0\n /// note: read all the variables through storageRead.sol\n\n /// note: vaultVariables contains vault variables which need regular updates through transactions\n /// First 1 bit => 0 => re-entrancy. If 0 then allow transaction to go, else throw.\n /// Next 1 bit => 1 => Is the current active branch liquidated? If true then check the branch's minima tick before creating a new position\n /// If the new tick is greater than minima tick then initialize a new branch, make that as current branch & do proper linking\n /// Next 1 bit => 2 => sign of topmost tick (0 -> negative; 1 -> positive)\n /// Next 19 bits => 3-21 => absolute value of topmost tick\n /// Next 30 bits => 22-51 => current branch ID\n /// Next 30 bits => 52-81 => total branch ID\n /// Next 64 bits => 82-145 => Total supply\n /// Next 64 bits => 146-209 => Total borrow\n /// Next 32 bits => 210-241 => Total positions\n uint256 internal vaultVariables;\n\n /// note: vaultVariables2 contains variables which do not update on every transaction. So mainly admin/auth set amount\n /// First 16 bits => 0-15 => supply rate magnifier; 10000 = 1x (Here 16 bits should be more than enough)\n /// Next 16 bits => 16-31 => borrow rate magnifier; 10000 = 1x (Here 16 bits should be more than enough)\n /// Next 10 bits => 32-41 => collateral factor. 800 = 0.8 = 80% (max precision of 0.1%)\n /// Next 10 bits => 42-51 => liquidation Threshold. 900 = 0.9 = 90% (max precision of 0.1%)\n /// Next 10 bits => 52-61 => liquidation Max Limit. 950 = 0.95 = 95% (max precision of 0.1%) (above this 100% liquidation can happen)\n /// Next 10 bits => 62-71 => withdraw gap. 100 = 0.1 = 10%. (max precision of 0.1%) (max 7 bits can also suffice for the requirement here of 0.1% to 10%). Needed to save some limits on withdrawals so liquidate can work seamlessly.\n /// Next 10 bits => 72-81 => liquidation penalty. 100 = 0.01 = 1%. (max precision of 0.01%) (max liquidation penantly can be 10.23%). Applies when tick is in between liquidation Threshold & liquidation Max Limit.\n /// Next 10 bits => 82-91 => borrow fee. 100 = 0.01 = 1%. (max precision of 0.01%) (max borrow fee can be 10.23%). Fees on borrow.\n /// Next 4 bits => 92-95 => empty\n /// Next 160 bits => 96-255 => Oracle address\n uint256 internal vaultVariables2;\n\n /// note: stores absorbed liquidity\n /// First 128 bits raw debt amount\n /// last 128 bits raw col amount\n uint256 internal absorbedLiquidity;\n\n /// position index => position data uint\n /// if the entire variable is 0 (meaning not initialized) at the start that means no position at all\n /// First 1 bit => 0 => position type (0 => borrow position; 1 => supply position)\n /// Next 1 bit => 1 => sign of user's tick (0 => negative; 1 => positive)\n /// Next 19 bits => 2-20 => absolute value of user's tick\n /// Next 24 bits => 21-44 => user's tick's id\n /// Below we are storing user's collateral & not debt, because the position can also be only collateral with no tick but it can never be only debt\n /// Next 64 bits => 45-108 => user's supply amount. Debt will be calculated through supply & ratio.\n /// Next 64 bits => 109-172 => user's dust debt amount. User's net debt = total debt - dust amount. Total debt is calculated through supply & ratio\n /// User won't pay any extra interest on dust debt & hence we will not show it as a debt on UI. For user's there's no dust.\n mapping(uint256 => uint256) internal positionData;\n\n /// Tick has debt only keeps data of non liquidated positions. liquidated tick's data stays in branch itself\n /// tick parent => uint (represents bool for 256 children)\n /// parent of (i)th tick:-\n /// if (i>=0) (i / 256);\n /// else ((i + 1) / 256) - 1\n /// first bit of the variable is the smallest tick & last bit is the biggest tick of that slot\n mapping(int256 => uint256) internal tickHasDebt;\n\n /// mapping tickId => tickData\n /// Tick related data. Total debt & other things\n /// First bit => 0 => If 1 then liquidated else not liquidated\n /// Next 24 bits => 1-24 => Total IDs. ID should start from 1.\n /// If not liquidated:\n /// Next 64 bits => 25-88 => raw debt\n /// If liquidated\n /// The below 3 things are of last ID. This is to be updated when user creates a new position\n /// Next 1 bit => 25 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated (100% liquidated)\n /// Next 30 bits => 26-55 => branch ID where this tick got liquidated\n /// Next 50 bits => 56-105 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n mapping(int256 => uint256) internal tickData;\n\n /// tick id => previous tick id liquidation data. ID starts from 1\n /// One tick ID contains 3 IDs of 80 bits in it, holding liquidation data of previously active but liquidated ticks\n /// 81 bits data below\n /// #### First 85 bits ####\n /// 1st bit => 0 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 1-30 => branch ID where this tick got liquidated\n /// Next 50 bits => 31-80 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n /// #### Second 85 bits ####\n /// 85th bit => 85 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 86-115 => branch ID where this tick got liquidated\n /// Next 50 bits => 116-165 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n /// #### Third 85 bits ####\n /// 170th bit => 170 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 171-200 => branch ID where this tick got liquidated\n /// Next 50 bits => 201-250 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n mapping(int256 => mapping(uint256 => uint256)) internal tickId;\n\n /// mapping branchId => branchData\n /// First 2 bits => 0-1 => if 0 then not liquidated, if 1 then liquidated, if 2 then merged, if 3 then closed\n /// merged means the branch is merged into it's base branch\n /// closed means all the users are 100% liquidated\n /// Next 1 bit => 2 => minima tick sign of this branch. Will only be there if any liquidation happened.\n /// Next 19 bits => 3-21 => minima tick of this branch. Will only be there if any liquidation happened.\n /// Next 30 bits => 22-51 => Partials of minima tick of branch this is connected to. 0 if master branch.\n /// Next 64 bits => 52-115 Debt liquidity at this branch. Similar to last's top tick data. Remaining debt will move here from tickData after first liquidation\n /// If not merged\n /// Next 50 bits => 116-165 => Debt factor or of this branch. (35 bits coefficient | 15 bits expansion)\n /// If merged\n /// Next 50 bits => 116-165 => Connection/adjustment debt factor of this branch with the next branch.\n /// If closed\n /// Next 50 bits => 116-165 => Debt factor as 0. As all the user's positions are now fully gone\n /// following values are present always again (merged / not merged / closed)\n /// Next 30 bits => 166-195 => Branch's ID with which this branch is connected. If 0 then that means this is the master branch\n /// Next 1 bit => 196 => sign of minima tick of branch this is connected to. 0 if master branch.\n /// Next 19 bits => 197-215 => minima tick of branch this is connected to. 0 if master branch.\n mapping(uint256 => uint256) internal branchData;\n\n /// Exchange prices are in 1e12\n /// First 64 bits => 0-63 => Liquidity's collateral token supply exchange price\n /// First 64 bits => 64-127 => Liquidity's debt token borrow exchange price\n /// First 64 bits => 128-191 => Vault's collateral token supply exchange price\n /// First 64 bits => 192-255 => Vault's debt token borrow exchange price\n uint256 internal rates;\n\n /// address of rebalancer\n address internal rebalancer;\n\n uint256 internal absorbedDustDebt;\n}\n"
|
||
},
|
||
"contracts/protocols/vault/vaultT1/coreModule/constantVariables.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidVaultFactory } from \"../../interfaces/iVaultFactory.sol\";\nimport { IFluidLiquidity } from \"../../../../liquidity/interfaces/iLiquidity.sol\";\nimport { StorageRead } from \"../../../../libraries/storageRead.sol\";\n\nimport { Structs } from \"./structs.sol\";\n\ninterface TokenInterface {\n function decimals() external view returns (uint8);\n}\n\ncontract ConstantVariables is StorageRead, Structs {\n /***********************************|\n | Constant Variables |\n |__________________________________*/\n\n address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n /// @dev collateral token address\n address internal immutable SUPPLY_TOKEN;\n /// @dev borrow token address\n address internal immutable BORROW_TOKEN;\n\n /// @dev Token decimals. For example wETH is 18 decimals\n uint8 internal immutable SUPPLY_DECIMALS;\n /// @dev Token decimals. For example USDC is 6 decimals\n uint8 internal immutable BORROW_DECIMALS;\n\n /// @dev VaultT1 AdminModule implemenation address\n address internal immutable ADMIN_IMPLEMENTATION;\n\n /// @dev VaultT1 Secondary implemenation (main2.sol) address\n address internal immutable SECONDARY_IMPLEMENTATION;\n\n /// @dev liquidity proxy contract address\n IFluidLiquidity public immutable LIQUIDITY;\n\n /// @dev vault factory contract address\n IFluidVaultFactory public immutable VAULT_FACTORY;\n\n uint public immutable VAULT_ID;\n\n uint internal constant X8 = 0xff;\n uint internal constant X10 = 0x3ff;\n uint internal constant X16 = 0xffff;\n uint internal constant X19 = 0x7ffff;\n uint internal constant X20 = 0xfffff;\n uint internal constant X24 = 0xffffff;\n uint internal constant X25 = 0x1ffffff;\n uint internal constant X30 = 0x3fffffff;\n uint internal constant X35 = 0x7ffffffff;\n uint internal constant X50 = 0x3ffffffffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n uint internal constant X96 = 0xffffffffffffffffffffffff;\n uint internal constant X128 = 0xffffffffffffffffffffffffffffffff;\n\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev slot ids in Liquidity contract. Helps in low gas fetch from liquidity contract by skipping delegate call\n bytes32 internal immutable LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT;\n bytes32 internal immutable LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT;\n bytes32 internal immutable LIQUIDITY_USER_SUPPLY_SLOT;\n bytes32 internal immutable LIQUIDITY_USER_BORROW_SLOT;\n\n /// @notice returns all Vault constants\n function constantsView() external view returns (ConstantViews memory constantsView_) {\n constantsView_.liquidity = address(LIQUIDITY);\n constantsView_.factory = address(VAULT_FACTORY);\n constantsView_.adminImplementation = ADMIN_IMPLEMENTATION;\n constantsView_.secondaryImplementation = SECONDARY_IMPLEMENTATION;\n constantsView_.supplyToken = SUPPLY_TOKEN;\n constantsView_.borrowToken = BORROW_TOKEN;\n constantsView_.supplyDecimals = SUPPLY_DECIMALS;\n constantsView_.borrowDecimals = BORROW_DECIMALS;\n constantsView_.vaultId = VAULT_ID;\n constantsView_.liquiditySupplyExchangePriceSlot = LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT;\n constantsView_.liquidityBorrowExchangePriceSlot = LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT;\n constantsView_.liquidityUserSupplySlot = LIQUIDITY_USER_SUPPLY_SLOT;\n constantsView_.liquidityUserBorrowSlot = LIQUIDITY_USER_BORROW_SLOT;\n }\n\n constructor(ConstantViews memory constants_) {\n LIQUIDITY = IFluidLiquidity(constants_.liquidity);\n VAULT_FACTORY = IFluidVaultFactory(constants_.factory);\n VAULT_ID = constants_.vaultId;\n\n SUPPLY_TOKEN = constants_.supplyToken;\n BORROW_TOKEN = constants_.borrowToken;\n SUPPLY_DECIMALS = constants_.supplyDecimals;\n BORROW_DECIMALS = constants_.borrowDecimals;\n\n // @dev those slots are calculated in the deploymentLogics / VaultFactory\n LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT = constants_.liquiditySupplyExchangePriceSlot;\n LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT = constants_.liquidityBorrowExchangePriceSlot;\n LIQUIDITY_USER_SUPPLY_SLOT = constants_.liquidityUserSupplySlot;\n LIQUIDITY_USER_BORROW_SLOT = constants_.liquidityUserBorrowSlot;\n\n ADMIN_IMPLEMENTATION = constants_.adminImplementation;\n SECONDARY_IMPLEMENTATION = constants_.secondaryImplementation;\n }\n}\n"
|
||
},
|
||
"contracts/protocols/vault/vaultT1/coreModule/events.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Events {\n /// @notice emitted when an operate() method is executed that changes collateral (`colAmt_`) / debt (debtAmt_`)\n /// amount for a `user_` position with `nftId_`. Receiver of any funds is the address `to_`.\n event LogOperate(address user_, uint256 nftId_, int256 colAmt_, int256 debtAmt_, address to_);\n\n /// @notice emitted when the exchange prices are updated in storage.\n event LogUpdateExchangePrice(uint256 supplyExPrice_, uint256 borrowExPrice_);\n\n /// @notice emitted when a liquidation has been executed.\n event LogLiquidate(address liquidator_, uint256 colAmt_, uint256 debtAmt_, address to_);\n\n /// @notice emitted when `absorb()` was executed to absorb bad debt.\n event LogAbsorb(uint colAbsorbedRaw_, uint debtAbsorbedRaw_);\n\n /// @notice emitted when a `rebalance()` has been executed, balancing out total supply / borrow between Vault\n /// and Fluid Liquidity pools.\n /// if `colAmt_` is positive then loss, meaning transfer from rebalancer address to vault and deposit.\n /// if `colAmt_` is negative then profit, meaning withdrawn from vault and sent to rebalancer address.\n /// if `debtAmt_` is positive then profit, meaning borrow from vault and sent to rebalancer address.\n /// if `debtAmt_` is negative then loss, meaning transfer from rebalancer address to vault and payback.\n event LogRebalance(int colAmt_, int debtAmt_);\n}\n"
|
||
},
|
||
"contracts/protocols/vault/vaultT1/coreModule/helpers.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"../common/variables.sol\";\nimport { ConstantVariables } from \"./constantVariables.sol\";\nimport { Events } from \"./events.sol\";\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { BigMathVault } from \"../../../../libraries/bigMathVault.sol\";\nimport { LiquidityCalcs } from \"../../../../libraries/liquidityCalcs.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { Error } from \"../../error.sol\";\n\n/// @dev Fluid vault protocol helper methods. Mostly used for `operate()` and `liquidate()` methods of CoreModule.\nabstract contract Helpers is Variables, ConstantVariables, Events, Error {\n using BigMathMinified for uint256;\n using BigMathVault for uint256;\n\n /// @notice Calculates new vault exchange prices. Does not update values in storage.\n /// @param vaultVariables2_ exactly same as vaultVariables2 from storage\n /// @return liqSupplyExPrice_ latest liquidity's supply token supply exchange price\n /// @return liqBorrowExPrice_ latest liquidity's borrow token borrow exchange price\n /// @return vaultSupplyExPrice_ latest vault's supply token exchange price\n /// @return vaultBorrowExPrice_ latest vault's borrow token exchange price\n function updateExchangePrices(\n uint256 vaultVariables2_\n )\n public\n view\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n )\n {\n // Fetching last stored rates\n uint rates_ = rates;\n\n (liqSupplyExPrice_, ) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT)\n );\n (, liqBorrowExPrice_) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT)\n );\n\n uint256 oldLiqSupplyExPrice_ = (rates_ & X64);\n uint256 oldLiqBorrowExPrice_ = ((rates_ >> 64) & X64);\n if (liqSupplyExPrice_ < oldLiqSupplyExPrice_ || liqBorrowExPrice_ < oldLiqBorrowExPrice_) {\n // new liquidity exchange price is < than the old one. liquidity exchange price should only ever increase.\n // If not, something went wrong and avoid proceeding with unknown outcome.\n revert FluidVaultError(ErrorTypes.VaultT1__LiquidityExchangePriceUnexpected);\n }\n\n // liquidity Exchange Prices always increases in next block. Hence substraction with old will never be negative\n // uint64 * 1e18 is the max the number that could be\n unchecked {\n // Calculating increase in supply exchange price w.r.t last stored liquidity's exchange price\n // vaultSupplyExPrice_ => supplyIncreaseInPercent_\n vaultSupplyExPrice_ = ((((liqSupplyExPrice_ * 1e18) / oldLiqSupplyExPrice_) - 1e18) *\n (vaultVariables2_ & X16)) / 10000; // supply rate magnifier\n\n // Calculating increase in borrow exchange price w.r.t last stored liquidity's exchange price\n // vaultBorrowExPrice_ => borrowIncreaseInPercent_\n vaultBorrowExPrice_ = ((((liqBorrowExPrice_ * 1e18) / oldLiqBorrowExPrice_) - 1e18) *\n ((vaultVariables2_ >> 16) & X16)) / 10000; // borrow rate magnifier\n\n // It's extremely hard the exchange prices to overflow even in 100 years but if it does it's not an\n // issue here as we are not updating on storage\n // (rates_ >> 128) & X64) -> last stored vault's supply token exchange price\n vaultSupplyExPrice_ = (((rates_ >> 128) & X64) * (1e18 + vaultSupplyExPrice_)) / 1e18;\n // (rates_ >> 192) -> last stored vault's borrow token exchange price (no need to mask with & X64 as it is anyway max 64 bits)\n vaultBorrowExPrice_ = ((rates_ >> 192) * (1e18 + vaultBorrowExPrice_)) / 1e18;\n }\n }\n\n /// note admin module is also calling this function self call\n /// @dev updating exchange price on storage. Only need to update on storage when changing supply or borrow magnifier\n function updateExchangePricesOnStorage()\n public\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n )\n {\n (liqSupplyExPrice_, liqBorrowExPrice_, vaultSupplyExPrice_, vaultBorrowExPrice_) = updateExchangePrices(\n vaultVariables2\n );\n\n if (\n liqSupplyExPrice_ > X64 || liqBorrowExPrice_ > X64 || vaultSupplyExPrice_ > X64 || vaultBorrowExPrice_ > X64\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExchangePriceOverFlow);\n }\n\n // Updating in storage\n rates =\n liqSupplyExPrice_ |\n (liqBorrowExPrice_ << 64) |\n (vaultSupplyExPrice_ << 128) |\n (vaultBorrowExPrice_ << 192);\n\n emit LogUpdateExchangePrice(vaultSupplyExPrice_, vaultBorrowExPrice_);\n }\n\n /// @dev fetches new user's position after liquidation. The new liquidated position's debt is decreased by 0.01%\n /// to make sure that branch's liquidity never becomes 0 as if it would have gotten 0 then there will be multiple cases that we would need to tackle.\n /// @param positionTick_ position's tick when it was last updated through operate\n /// @param positionTickId_ position's tick Id. This stores the debt factor and branch to make the first connection\n /// @param positionRawDebt_ position's raw debt when it was last updated through operate\n /// @param tickData_ position's tick's tickData just for minor comparison to know if data is moved to tick Id or is still in tick data\n /// @return final tick position after all the liquidation\n /// @return final debt of position after all the liquidation\n /// @return positionRawCol_ final collateral of position after all the liquidation\n /// @return branchId_ final branch's ID where the position is at currently\n /// @return branchData_ final branch's data where the position is at currently\n function fetchLatestPosition(\n int256 positionTick_,\n uint256 positionTickId_,\n uint256 positionRawDebt_,\n uint256 tickData_\n )\n public\n view\n returns (\n int256, // positionTick_\n uint256, // positionRawDebt_\n uint256 positionRawCol_,\n uint256 branchId_,\n uint256 branchData_\n )\n {\n uint256 initialPositionRawDebt_ = positionRawDebt_;\n uint256 connectionFactor_;\n bool isFullyLiquidated_;\n\n // Checking if tick's total ID = user's tick ID\n if (((tickData_ >> 1) & X24) == positionTickId_) {\n // fetching from tick data itself\n isFullyLiquidated_ = ((tickData_ >> 25) & 1) == 1;\n branchId_ = (tickData_ >> 26) & X30;\n connectionFactor_ = (tickData_ >> 56) & X50;\n } else {\n {\n uint256 tickLiquidationData_;\n unchecked {\n // Fetching tick's liquidation data. One variable contains data of 3 IDs. Tick Id mapping is starting from 1.\n tickLiquidationData_ =\n tickId[positionTick_][(positionTickId_ + 2) / 3] >>\n (((positionTickId_ + 2) % 3) * 85);\n }\n\n isFullyLiquidated_ = (tickLiquidationData_ & 1) == 1;\n branchId_ = (tickLiquidationData_ >> 1) & X30;\n connectionFactor_ = (tickLiquidationData_ >> 31) & X50;\n }\n }\n\n // data of branch\n branchData_ = branchData[branchId_];\n\n if (isFullyLiquidated_) {\n positionTick_ = type(int).min;\n positionRawDebt_ = 0;\n } else {\n // Below information about connection debt factor\n // If branch is merged, Connection debt factor is used to multiply in order to get perfect liquidation of user\n // For example: Considering user was at the top.\n // In first branch, the user liquidated to debt factor 0.5 and then branch got merged (branching starting from 1)\n // In second branch, it got liquidated to 0.4 but when the above branch merged the debt factor on this branch was 0.6\n // Meaning on 1st branch, user got liquidated by 50% & on 2nd by 33.33%. So a total of 66.6%.\n // What we will set a connection factor will be 0.6/0.5 = 1.2\n // So now to get user's position, this is what we'll do:\n // finalDebt = (0.4 / (1 * 1.2)) * debtBeforeLiquidation\n // 0.4 is current active branch's minima debt factor\n // 1 is debt factor from where user started\n // 1.2 is connection factor which we found out through 0.6 / 0.5\n while ((branchData_ & 3) == 2) {\n // If true then the branch is merged\n\n // userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor aka adjustmentDebtFactor\n connectionFactor_ = connectionFactor_.mulBigNumber(((branchData_ >> 116) & X50));\n if (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR) break; // user ~100% liquidated\n // Note we don't need updated branch data in case of 100% liquidated so saving gas for fetching it\n\n // Fetching new branch data\n branchId_ = (branchData_ >> 166) & X30; // Link to base branch of current branch\n branchData_ = branchData[branchId_];\n }\n // When the while loop breaks meaning the branch now has minima Debt Factor or is a closed branch;\n\n if (((branchData_ & 3) == 3) || (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR)) {\n // Branch got closed (or user liquidated ~100%). Hence make the user's position 0\n // Rare cases to get into this situation\n // Branch can get close often but once closed it's tricky that some user might come iterating through there\n // If a user comes then that user will be very mini user like some cents probably\n positionTick_ = type(int).min;\n positionRawDebt_ = 0;\n } else {\n // If branch is not merged, the main branch it's connected to then it'll have minima debt factor\n\n // position debt = debt * base branch minimaDebtFactor / connectionFactor\n positionRawDebt_ = positionRawDebt_.mulDivNormal(\n (branchData_ >> 116) & X50, // minimaDebtFactor\n connectionFactor_\n );\n\n unchecked {\n // Reducing user's liquidity by 0.01% if user got liquidated.\n // As this will make sure that the branch always have some debt even if all liquidated user left\n // This saves a lot more logics & consideration on Operate function\n // if we don't do this then we have to add logics related to closing the branch and factor connections accordingly.\n if (positionRawDebt_ > (initialPositionRawDebt_ / 100)) {\n positionRawDebt_ = (positionRawDebt_ * 9999) / 10000;\n } else {\n // if user debt reduced by more than 99% in liquidation then making user as fully liquidated\n positionRawDebt_ = 0;\n }\n }\n\n {\n if (positionRawDebt_ > 0) {\n // positionTick_ -> read minima tick of branch\n unchecked {\n positionTick_ = branchData_ & 4 == 4\n ? int((branchData_ >> 3) & X19)\n : -int((branchData_ >> 3) & X19);\n }\n // Calculating user's collateral\n uint256 ratioAtTick_ = TickMath.getRatioAtTick(int24(positionTick_));\n uint256 ratioOneLess_;\n unchecked {\n ratioOneLess_ = (ratioAtTick_ * 10000) / 10015;\n }\n // formula below for better readability:\n // length = ratioAtTick_ - ratioOneLess_\n // ratio = ratioOneLess_ + (length * positionPartials_) / X30\n // positionRawCol_ = (positionRawDebt_ * (1 << 96)) / ratio_\n positionRawCol_ =\n (positionRawDebt_ * TickMath.ZERO_TICK_SCALED_RATIO) /\n (ratioOneLess_ + ((ratioAtTick_ - ratioOneLess_) * ((branchData_ >> 22) & X30)) / X30);\n } else {\n positionTick_ = type(int).min;\n }\n }\n }\n }\n return (positionTick_, positionRawDebt_, positionRawCol_, branchId_, branchData_);\n }\n\n /// @dev sets `tick_` as having debt or no debt in storage `tickHasDebt` depending on `addOrRemove_`\n /// @param tick_ tick to add or remove from tickHasDebt\n /// @param addOrRemove_ if true then add else remove\n function _updateTickHasDebt(int tick_, bool addOrRemove_) internal {\n // Positive mapID_ starts from 0 & above and negative starts below 0.\n // tick 0 to 255 will have mapId_ as 0 while tick -256 to -1 will have mapId_ as -1.\n unchecked {\n int mapId_ = tick_ < 0 ? ((tick_ + 1) / 256) - 1 : tick_ / 256;\n\n // in case of removing:\n // (tick == 255) tickHasDebt[mapId_] - 1 << 255\n // (tick == 0) tickHasDebt[mapId_] - 1 << 0\n // (tick == -1) tickHasDebt[mapId_] - 1 << 255\n // (tick == -256) tickHasDebt[mapId_] - 1 << 0\n // in case of adding:\n // (tick == 255) tickHasDebt[mapId_] - 1 << 255\n // (tick == 0) tickHasDebt[mapId_] - 1 << 0\n // (tick == -1) tickHasDebt[mapId_] - 1 << 255\n // (tick == -256) tickHasDebt[mapId_] - 1 << 0\n uint position_ = uint(tick_ - (mapId_ * 256));\n\n tickHasDebt[mapId_] = addOrRemove_\n ? tickHasDebt[mapId_] | (1 << position_)\n : tickHasDebt[mapId_] & ~(1 << position_);\n }\n }\n\n /// @dev gets next perfect top tick (tick which is not liquidated)\n /// @param topTick_ current top tick which will no longer be top tick\n /// @return nextTick_ next top tick which will become the new top tick\n function _fetchNextTopTick(int topTick_) internal view returns (int nextTick_) {\n int mapId_;\n uint tickHasDebt_;\n\n unchecked {\n mapId_ = topTick_ < 0 ? ((topTick_ + 1) / 256) - 1 : topTick_ / 256;\n uint bitsToRemove_ = uint(-topTick_ + (mapId_ * 256 + 256));\n // Removing current top tick from tickHasDebt\n tickHasDebt_ = (tickHasDebt[mapId_] << bitsToRemove_) >> bitsToRemove_;\n\n // For last user remaining in vault there could be a lot of iterations in the while loop.\n // Chances of this to happen is extremely low (like ~0%)\n while (true) {\n if (tickHasDebt_ > 0) {\n nextTick_ = mapId_ * 256 + int(tickHasDebt_.mostSignificantBit()) - 1;\n break;\n }\n\n // Reducing mapId_ by 1 in every loop; if it reaches to -129 then no filled tick exist, meaning it's the last tick\n if (--mapId_ == -129) {\n nextTick_ = type(int).min;\n break;\n }\n\n tickHasDebt_ = tickHasDebt[mapId_];\n }\n }\n }\n\n /// @dev adding debt to a particular tick\n /// @param totalColRaw_ total raw collateral of position\n /// @param netDebtRaw_ net raw debt (total debt - dust debt)\n /// @return tick_ tick where the debt is being added\n /// @return tickId_ tick current id\n /// @return userRawDebt_ user's total raw debt\n /// @return rawDust_ dust debt used for adjustment\n function _addDebtToTickWrite(\n uint256 totalColRaw_,\n uint256 netDebtRaw_ // debtRaw - dust\n ) internal returns (int256 tick_, uint256 tickId_, uint256 userRawDebt_, uint256 rawDust_) {\n if (netDebtRaw_ < 10000) {\n // thrown if user's debt is too low\n revert FluidVaultError(ErrorTypes.VaultT1__UserDebtTooLow);\n }\n // tick_ & ratio_ returned from library is round down. Hence increasing it by 1 and increasing ratio by 1 tick.\n uint ratio_ = (netDebtRaw_ * TickMath.ZERO_TICK_SCALED_RATIO) / totalColRaw_;\n (tick_, ratio_) = TickMath.getTickAtRatio(ratio_);\n unchecked {\n ++tick_;\n ratio_ = (ratio_ * 10015) / 10000;\n }\n userRawDebt_ = (ratio_ * totalColRaw_) >> 96;\n rawDust_ = userRawDebt_ - netDebtRaw_;\n\n // Current state of tick\n uint256 tickData_ = tickData[tick_];\n tickId_ = (tickData_ >> 1) & X24;\n\n uint tickNewDebt_;\n if (tickId_ > 0 && tickData_ & 1 == 0) {\n // Current debt in the tick\n uint256 tickExistingRawDebt_ = (tickData_ >> 25) & X64;\n tickExistingRawDebt_ = (tickExistingRawDebt_ >> 8) << (tickExistingRawDebt_ & X8);\n\n // Tick's already initialized and not liquidated. Hence simply add the debt\n tickNewDebt_ = tickExistingRawDebt_ + userRawDebt_;\n if (tickExistingRawDebt_ == 0) {\n // Adding tick into tickHasDebt\n _updateTickHasDebt(tick_, true);\n }\n } else {\n // Liquidation happened or tick getting initialized for the very first time.\n if (tickId_ > 0) {\n // Meaning a liquidation happened. Hence move the data to tickID\n unchecked {\n uint tickMap_ = (tickId_ + 2) / 3;\n // Adding 2 in ID so we can get right mapping ID. For example for ID 1, 2 & 3 mapping should be 1 and so on..\n // For example shift for id 1 should be 0, for id 2 should be 85, for id 3 it should be 170 and so on..\n tickId[tick_][tickMap_] =\n tickId[tick_][tickMap_] |\n ((tickData_ >> 25) << (((tickId_ + 2) % 3) * 85));\n }\n }\n // Increasing total ID by one\n unchecked {\n ++tickId_;\n }\n tickNewDebt_ = userRawDebt_;\n\n // Adding tick into tickHasDebt\n _updateTickHasDebt(tick_, true);\n }\n if (tickNewDebt_ < 10000) {\n // thrown if tick's debt/liquidity is too low\n revert FluidVaultError(ErrorTypes.VaultT1__TickDebtTooLow);\n }\n tickData[tick_] = (tickId_ << 1) | (tickNewDebt_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 25);\n }\n\n /// @dev sets new top tick. If it comes to this function then that means current top tick is perfect tick.\n /// if next top tick is liquidated then unitializes the current non liquidated branch and make the liquidated branch as current branch\n /// @param topTick_ current top tick\n /// @param vaultVariables_ vaultVariables of storage but with newer updates\n /// @return newVaultVariables_ newVaultVariables_ updated vault variable internally to this function\n /// @return newTopTick_ new top tick\n function _setNewTopTick(\n int topTick_,\n uint vaultVariables_\n ) internal returns (uint newVaultVariables_, int newTopTick_) {\n // This function considers that the current top tick was not liquidated\n // Overall flow of function:\n // if new top tick liquidated (aka base branch's minima tick) -> Close the current branch and make base branch as current branch\n // if new top tick not liquidated -> update things in current branch.\n // if new top tick is not liquidated and same tick exist in base branch then tick is considered as not liquidated.\n\n uint branchId_ = (vaultVariables_ >> 22) & X30; // branch id of current branch\n\n uint256 branchData_ = branchData[branchId_];\n int256 baseBranchMinimaTick_;\n if ((branchData_ >> 196) & 1 == 1) {\n baseBranchMinimaTick_ = int((branchData_ >> 197) & X19);\n } else {\n unchecked {\n baseBranchMinimaTick_ = -int((branchData_ >> 197) & X19);\n }\n if (baseBranchMinimaTick_ == 0) {\n // meaning the current branch is the master branch\n baseBranchMinimaTick_ = type(int).min;\n }\n }\n\n // Returns type(int).min if no top tick exist\n int nextTopTickNotLiquidated_ = _fetchNextTopTick(topTick_);\n\n newTopTick_ = baseBranchMinimaTick_ > nextTopTickNotLiquidated_\n ? baseBranchMinimaTick_\n : nextTopTickNotLiquidated_;\n\n if (newTopTick_ == type(int).min) {\n // if this happens that means this was the last user of the vault :(\n vaultVariables_ = vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001;\n } else if (newTopTick_ == nextTopTickNotLiquidated_) {\n // New top tick exist in current non liquidated branch\n if (newTopTick_ < 0) {\n unchecked {\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001) |\n (uint(-newTopTick_) << 3);\n }\n } else {\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001) |\n 4 | // setting top tick as positive\n (uint(newTopTick_) << 3);\n }\n } else {\n // if this happens that means base branch exists & is the next top tick\n // Remove current non liquidated branch as active.\n // Not deleting here as it's going to get initialize again whenever a new top tick comes\n branchData[branchId_] = 0;\n // Inserting liquidated branch's minima tick\n unchecked {\n vaultVariables_ =\n (vaultVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00000000000000000001) |\n 2 | // Setting top tick as liquidated\n (((branchData_ >> 196) & X20) << 2) | // new current top tick = base branch minima tick\n (((branchData_ >> 166) & X30) << 22) | // new current branch id = base branch id\n ((branchId_ - 1) << 52); // reduce total branch id by 1\n }\n }\n\n newVaultVariables_ = vaultVariables_;\n }\n\n constructor(ConstantViews memory constants_) ConstantVariables(constants_) {}\n}\n"
|
||
},
|
||
"contracts/protocols/vault/vaultT1/coreModule/main.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n\nimport { IFluidOracle } from \"../../../../oracle/fluidOracle.sol\";\n\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { BigMathVault } from \"../../../../libraries/bigMathVault.sol\";\nimport { LiquidityCalcs } from \"../../../../libraries/liquidityCalcs.sol\";\nimport { SafeTransfer } from \"../../../../libraries/safeTransfer.sol\";\n\nimport { Helpers } from \"./helpers.sol\";\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\n\n/// @notice Fluid \"VaultT1\" (Vault Type 1). Fluid vault protocol main contract.\n/// Fluid Vault protocol is a borrow / lending protocol, allowing users to create collateral / borrow positions.\n/// All funds are deposited into / borrowed from Fluid Liquidity layer.\n/// Positions are represented through NFTs minted by the VaultFactory.\n/// Deployed by \"VaultFactory\" and linked together with VaultT1 AdminModule `ADMIN_IMPLEMENTATION` and\n/// FluidVaultT1Secondary (main2.sol) `SECONDARY_IMPLEMENTATION`.\n/// AdminModule & FluidVaultT1Secondary methods are delegateCalled, if the msg.sender has the required authorization.\n/// This contract links to an Oracle, which is used to assess collateral / debt value. Oracles implement the\n/// \"FluidOracle\" base contract and return the price in 1e27 precision.\n/// @dev For view methods / accessing data, use the \"VaultResolver\" periphery contract.\n//\n// vaults can only be deployed for tokens that are listed at Liquidity (constructor reverts otherwise\n// if either the exchange price for the supply token or the borrow token is still not set at Liquidity).\ncontract FluidVaultT1 is Helpers {\n using BigMathMinified for uint256;\n using BigMathVault for uint256;\n\n /// @dev Single function which handles supply, withdraw, borrow & payback\n /// @param nftId_ NFT ID for interaction. If 0 then create new NFT/position.\n /// @param newCol_ new collateral. If positive then deposit, if negative then withdraw, if 0 then do nohing\n /// @param newDebt_ new debt. If positive then borrow, if negative then payback, if 0 then do nohing\n /// @param to_ address where withdraw or borrow should go. If address(0) then msg.sender\n /// @return nftId_ if 0 then this returns the newly created NFT Id else returns the same NFT ID\n /// @return newCol_ final supply amount. Mainly if max withdraw using type(int).min then this is useful to get perfect amount else remain same as newCol_\n /// @return newDebt_ final borrow amount. Mainly if max payback using type(int).min then this is useful to get perfect amount else remain same as newDebt_\n function operate(\n uint256 nftId_, // if 0 then new position\n int256 newCol_, // if negative then withdraw\n int256 newDebt_, // if negative then payback\n address to_ // address at which the borrow & withdraw amount should go to. If address(0) then it'll go to msg.sender\n )\n public\n payable\n returns (\n uint256, // nftId_\n int256, // final supply amount. if - then withdraw\n int256 // final borrow amount. if - then payback\n )\n {\n uint256 vaultVariables_ = vaultVariables;\n // re-entrancy check\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n if (\n (newCol_ == 0 && newDebt_ == 0) ||\n // withdrawal or deposit cannot be too small\n ((newCol_ != 0) && (newCol_ > -10000 && newCol_ < 10000)) ||\n // borrow or payback cannot be too small\n ((newDebt_ != 0) && (newDebt_ > -10000 && newDebt_ < 10000))\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOperateAmount);\n }\n\n // Check msg.value aligns with input amounts if supply or borrow token is native token.\n // Note that it's not possible for a vault to have both supply token and borrow token as native token.\n if (SUPPLY_TOKEN == NATIVE_TOKEN && newCol_ > 0) {\n if (uint(newCol_) != msg.value) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n } else if (msg.value > 0) {\n if (!(BORROW_TOKEN == NATIVE_TOKEN && newDebt_ < 0)) {\n // msg.value sent along for withdraw, borrow, or non-native token operations\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n }\n\n OperateMemoryVars memory o_;\n // Temporary variables used as helpers at many places\n uint256 temp_;\n uint256 temp2_;\n int256 temp3_;\n\n o_.vaultVariables2 = vaultVariables2;\n\n temp_ = (vaultVariables_ >> 2) & X20;\n unchecked {\n o_.topTick = (temp_ == 0) ? type(int).min : ((temp_ & 1) == 1)\n ? int((temp_ >> 1) & X19)\n : -int((temp_ >> 1) & X19);\n }\n\n {\n // Fetching user's position\n if (nftId_ == 0) {\n // creating new position.\n o_.tick = type(int).min;\n // minting new NFT vault for user.\n nftId_ = VAULT_FACTORY.mint(VAULT_ID, msg.sender);\n // Adding 1 in total positions. Total positions cannot exceed 32bits as NFT minting checks for that\n unchecked {\n vaultVariables_ = vaultVariables_ + (1 << 210);\n }\n } else {\n // Updating existing position\n\n // checking owner only in case of withdraw or borrow\n if ((newCol_ < 0 || newDebt_ > 0) && (VAULT_FACTORY.ownerOf(nftId_) != msg.sender)) {\n revert FluidVaultError(ErrorTypes.VaultT1__NotAnOwner);\n }\n\n // temp_ => user's position data\n temp_ = positionData[nftId_];\n\n if (temp_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__NftNotOfThisVault);\n }\n // temp2_ => user's supply amount\n temp2_ = (temp_ >> 45) & X64;\n // Converting big number into normal number\n o_.colRaw = (temp2_ >> 8) << (temp2_ & X8);\n // temp2_ => user's dust debt amount\n temp2_ = (temp_ >> 109) & X64;\n // Converting big number into normal number\n o_.dustDebtRaw = (temp2_ >> 8) << (temp2_ & X8);\n\n // 1 is supply & 0 is borrow\n if (temp_ & 1 == 1) {\n // only supply position (has no debt)\n o_.tick = type(int).min;\n } else {\n // borrow position (has collateral & debt)\n unchecked {\n o_.tick = temp_ & 2 == 2 ? int((temp_ >> 2) & X19) : -int((temp_ >> 2) & X19);\n }\n o_.tickId = (temp_ >> 21) & X24;\n }\n }\n }\n\n // Get latest updated Position's debt & supply (if position is with debt -> not new / supply position)\n if (o_.tick > type(int).min) {\n // if entering this if statement then temp_ here will always be user's position data\n // extracting collateral exponent\n temp_ = (temp_ >> 45) & X8;\n // if exponent is > 0 then rounding up the collateral just for calculating debt\n unchecked {\n temp_ = temp_ == 0 ? (o_.colRaw + 1) : o_.colRaw + (1 << temp_);\n }\n // fetch current debt\n o_.debtRaw = ((TickMath.getRatioAtTick(int24(o_.tick)) * temp_) >> 96) + 1;\n\n // Tick data from user's tick\n temp_ = tickData[o_.tick];\n\n // Checking if tick is liquidated (first bit 1) OR if the total IDs of tick is greater than user's tick ID\n if (((temp_ & 1) == 1) || (((temp_ >> 1) & X24) > o_.tickId)) {\n // User got liquidated\n (\n // returns the position of the user if the user got liquidated.\n o_.tick,\n o_.debtRaw,\n o_.colRaw,\n temp2_, // final branchId from liquidation where position exist right now\n o_.branchData\n ) = fetchLatestPosition(o_.tick, o_.tickId, o_.debtRaw, temp_);\n\n if (o_.debtRaw > o_.dustDebtRaw) {\n // temp_ => branch's Debt\n temp_ = (o_.branchData >> 52) & X64;\n temp_ = (temp_ >> 8) << (temp_ & X8);\n\n // o_.debtRaw should always be < branch's Debt (temp_).\n // Taking margin (0.01%) in fetchLatestPosition to make sure it's always less\n temp_ -= o_.debtRaw;\n if (temp_ < 100) {\n // explicitly making sure that branch debt/liquidity doesn't get super low.\n temp_ = 100;\n }\n // Inserting updated branch's debt\n branchData[temp2_] =\n (o_.branchData & 0xfffffffffffffffffffffffffffffffffff0000000000000000fffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52);\n\n unchecked {\n // Converted positionRawDebt_ in net position debt\n o_.debtRaw -= o_.dustDebtRaw;\n }\n } else {\n // Liquidated 100% or almost 100%\n // absorbing dust debt\n absorbedDustDebt = absorbedDustDebt + o_.dustDebtRaw - o_.debtRaw;\n o_.debtRaw = 0;\n o_.colRaw = 0;\n }\n } else {\n // User didn't got liquidated\n // Removing user's debt from tick data\n // temp2_ => debt in tick\n temp2_ = (temp_ >> 25) & X64;\n // below require can fail when a user liquidity is extremely low (talking about way less than even $1)\n // adding require meaning this vault user won't be able to interact unless someone makes the liquidity in tick as non 0.\n // reason of adding is the tick has already removed from everywhere. Can removing it again break something? Better to simply remove that case entirely\n if (temp2_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__TickIsEmpty);\n }\n // Converting big number into normal number\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n // debtInTick (temp2_) < debtToRemove (o_.debtRaw) that means minor precision error. Hence make the debtInTick as 0.\n // The precision error can be caused with Bigmath library limiting the precision to 2**56.\n unchecked {\n temp2_ = o_.debtRaw < temp2_ ? temp2_ - o_.debtRaw : 0;\n }\n\n if (temp2_ < 10000) {\n temp2_ = 0;\n // if debt becomes 0 then remove from tick has debt\n\n if (o_.tick == o_.topTick) {\n // if tick is top tick then current top tick is perfect tick -> fetch & set new top tick\n\n // Updating new top tick in vaultVariables_ and topTick_\n (vaultVariables_, o_.topTick) = _setNewTopTick(o_.topTick, vaultVariables_);\n }\n\n // Removing from tickHasDebt\n _updateTickHasDebt(o_.tick, false);\n }\n\n tickData[o_.tick] = (temp_ & X25) | (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 25);\n\n // Converted positionRawDebt_ in net position debt\n o_.debtRaw -= o_.dustDebtRaw;\n }\n o_.dustDebtRaw = 0;\n }\n\n // Setting the current tick into old tick as the position tick is going to change now.\n o_.oldTick = o_.tick;\n o_.oldColRaw = o_.colRaw;\n o_.oldNetDebtRaw = o_.debtRaw;\n\n {\n (o_.liquidityExPrice, , o_.supplyExPrice, o_.borrowExPrice) = updateExchangePrices(o_.vaultVariables2);\n\n {\n // supply or withdraw\n if (newCol_ > 0) {\n // supply new col, rounding down\n o_.colRaw += (uint256(newCol_) * EXCHANGE_PRICES_PRECISION) / o_.supplyExPrice;\n // final user's collateral should not be above 2**128 bits\n if (o_.colRaw > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n } else if (newCol_ < 0) {\n // if withdraw equals type(int).min then max withdraw\n if (newCol_ > type(int128).min) {\n // partial withdraw, rounding up removing extra wei from collateral\n temp3_ = ((newCol_ * int(EXCHANGE_PRICES_PRECISION)) / int256(o_.supplyExPrice)) - 1;\n unchecked {\n if (uint256(-temp3_) > o_.colRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessCollateralWithdrawal);\n }\n o_.colRaw -= uint256(-temp3_);\n }\n } else if (newCol_ == type(int).min) {\n // max withdraw, rounding up:\n // adding +1 to negative withdrawAmount newCol_ for safe rounding (reducing withdraw)\n newCol_ = -(int256((o_.colRaw * o_.supplyExPrice) / EXCHANGE_PRICES_PRECISION)) + 1;\n o_.colRaw = 0;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n }\n }\n {\n // borrow or payback\n if (newDebt_ > 0) {\n // borrow new debt, rounding up adding extra wei in debt\n temp_ = ((uint(newDebt_) * EXCHANGE_PRICES_PRECISION) / o_.borrowExPrice) + 1;\n // if borrow fee is 0 then it'll become temp_ + 0.\n // Only adding fee in o_.debtRaw and not in newDebt_ as newDebt_ is debt that needs to be borrowed from Liquidity\n // as we have added fee in debtRaw hence it will get added in user's position & vault's total borrow.\n // It can be collected with rebalance function.\n o_.debtRaw += temp_ + (temp_ * ((o_.vaultVariables2 >> 82) & X10)) / 10000;\n // final user's debt should not be above 2**128 bits\n if (o_.debtRaw > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n } else if (newDebt_ < 0) {\n // if payback equals type(int).min then max payback\n if (newDebt_ > type(int128).min) {\n // partial payback.\n // temp3_ => newDebt_ in raw terms, safe rounding up negative amount to rounding reduce payback\n temp3_ = (newDebt_ * int256(EXCHANGE_PRICES_PRECISION)) / int256(o_.borrowExPrice) + 1;\n unchecked {\n temp3_ = -temp3_;\n if (uint256(temp3_) > o_.debtRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessDebtPayback);\n }\n o_.debtRaw -= uint256(temp3_);\n }\n } else if (newDebt_ == type(int).min) {\n // max payback, rounding up amount that will be transferred in to pay back full debt:\n // subtracting -1 of negative debtAmount newDebt_ for safe rounding (increasing payback)\n newDebt_ = -(int256((o_.debtRaw * o_.borrowExPrice) / EXCHANGE_PRICES_PRECISION)) - 1;\n o_.debtRaw = 0;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n }\n }\n }\n\n // if position has no collateral or debt and user sends type(int).min for withdraw and payback then this results in 0\n // there's is no issue if it stays 0 but better to throw here to avoid checking for potential issues if there could be\n if (newCol_ == 0 && newDebt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOperateAmount);\n }\n\n // Assign new tick\n if (o_.debtRaw > 0) {\n // updating tickHasDebt in the below function if required\n // o_.debtRaw here is updated to new debt raw incl. dust debt (not net debt)\n unchecked {\n (o_.tick, o_.tickId, o_.debtRaw, o_.dustDebtRaw) = _addDebtToTickWrite(\n o_.colRaw,\n ((o_.debtRaw * 1000000001) / 1000000000) + 1\n );\n }\n\n if (newDebt_ < 0) {\n // anyone can payback debt of any position\n // hence, explicitly checking the debt should decrease\n if ((o_.debtRaw - o_.dustDebtRaw) > o_.oldNetDebtRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidPaybackOrDeposit);\n }\n }\n if ((newCol_ > 0) && (newDebt_ == 0)) {\n // anyone can deposit collateral in any position\n // Hence, explicitly checking that new ratio should be less than old ratio\n if (\n (((o_.debtRaw - o_.dustDebtRaw) * TickMath.ZERO_TICK_SCALED_RATIO) / o_.colRaw) >\n ((o_.oldNetDebtRaw * TickMath.ZERO_TICK_SCALED_RATIO) / o_.oldColRaw)\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidPaybackOrDeposit);\n }\n }\n\n if (o_.tick >= o_.topTick) {\n // Updating topTick in storage\n // temp_ => tick to insert in vault variables\n unchecked {\n temp_ = o_.tick < 0 ? uint(-o_.tick) << 1 : (uint(o_.tick) << 1) | 1;\n }\n if (vaultVariables_ & 2 == 0) {\n // Current branch not liquidated. Hence, just update top tick\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000) |\n (temp_ << 2);\n } else {\n // Current branch liquidated\n // Initialize a new branch\n // temp2_ => totalBranchId_\n unchecked {\n temp2_ = ((vaultVariables_ >> 52) & X30) + 1; // would take 34 years to overflow if a new branch is created every second\n }\n // Connecting new active branch with current active branch which is now base branch\n // Current top tick is now base branch's minima tick\n branchData[temp2_] =\n (((vaultVariables_ >> 22) & X30) << 166) | // current branch id set as base branch id\n (((vaultVariables_ >> 2) & X20) << 196); // current top tick set as base branch minima tick\n // Updating new vault variables in memory with new branch\n vaultVariables_ =\n (vaultVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00000000000000000000) |\n (temp_ << 2) | // new top tick\n (temp2_ << 22) | // new branch id\n (temp2_ << 52); // total branch ids\n }\n }\n } else {\n // debtRaw_ remains 0 in this situation\n // This kind of position will not have any tick. Meaning it'll be a supply position.\n o_.tick = type(int).min;\n }\n\n {\n if (newCol_ < 0 || newDebt_ > 0) {\n // withdraw or borrow\n if (to_ == address(0)) {\n to_ = msg.sender;\n }\n\n unchecked {\n // if debt is greater than 0 & transaction includes borrow or withdraw (incl. combinations such as deposit + borrow etc.)\n // -> check collateral factor\n // calc for net debt can be unchecked as o_.dustDebtRaw can not be > o_.debtRaw:\n // o_.dustDebtRaw is the result of o_.debtRaw - x where x > 0 see _addDebtToTickWrite()\n\n // Only fetch oracle if position is getting riskier or if borrowing is involved\n // if user is withdrawing and paying back in the same transaction such that the final ratio\n // is lower than initial then as well no need to check oracle aka user is doing payback & withdraw or deleverage\n if (o_.debtRaw > 0 && (\n o_.oldTick <= o_.tick ||\n (o_.debtRaw - o_.dustDebtRaw) > (((o_.oldNetDebtRaw * 1000000001) / 1000000000) + 1)\n )\n ) {\n // Oracle returns price at 100% ratio.\n // converting oracle 160 bits into oracle address\n // temp_ => debt price w.r.t to col in 1e27\n temp_ = IFluidOracle(address(uint160(o_.vaultVariables2 >> 96))).getExchangeRateOperate();\n // Note if price would come back as 0 `getTickAtRatio` will fail\n\n // reverting if oracle price is too high or lower than 1e9 to avoid precision issues\n if (temp_ > 1e54 || temp_ < 1e9) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOraclePrice);\n }\n\n // Converting price in terms of raw amounts\n temp_ = (temp_ * o_.supplyExPrice) / o_.borrowExPrice;\n\n // capping oracle pricing to 1e45 (#487RGF783GF: id reference for other similar cases in codebase)\n // This means we are restricting collateral price to never go above 1e45\n // Above 1e45 precisions gets too low for calculations\n // This can will never happen for all good token pairs (for example, WBTC/DAI pair when WBTC price is $1M, oracle price will come as 1e43)\n // Restricting oracle price doesn't pose any risk to protocol as we are capping collateral price, meaning if price is above 1e45\n // user is simply not able to borrow more\n if (temp_ > 1e45) {\n temp_ = 1e45;\n }\n\n // temp2_ => ratio at CF. CF is in 3 decimals. 900 = 90%\n temp2_ = ((temp_ * ((o_.vaultVariables2 >> 32) & X10)) / 1000);\n\n // Price from oracle is in 1e27 decimals. Converting it into (1 << 96) decimals\n temp2_ = ((temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27);\n\n // temp3_ => tickAtCF_\n (temp3_, ) = TickMath.getTickAtRatio(temp2_);\n if (o_.tick > temp3_) {\n // Above CF, user should only be allowed to reduce ratio either by paying debt or by depositing more collateral\n // Not comparing collateral as user can potentially use safe/deleverage to reduce tick & debt.\n // On use of safe/deleverage, collateral will decrease but debt will decrease as well making the overall position safer.\n revert FluidVaultError(ErrorTypes.VaultT1__PositionAboveCF);\n }\n }\n }\n\n }\n }\n\n {\n // Updating user's new position on storage\n // temp_ => tick to insert as user position tick\n if (o_.tick > type(int).min) {\n unchecked {\n temp_ = o_.tick < 0 ? (uint(-o_.tick) << 1) : ((uint(o_.tick) << 1) | 1);\n }\n } else {\n // if positionTick_ = type(int).min OR positionRawDebt_ == 0 then that means it's only supply position\n // (for case of positionRawDebt_ == 0, tick is set to type(int).min further up)\n temp_ = 0;\n }\n\n positionData[nftId_] =\n ((temp_ == 0) ? 1 : 0) | // setting if supply only position (1) or not (first bit)\n (temp_ << 1) |\n (o_.tickId << 21) |\n (o_.colRaw.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 45) |\n // dust debt is rounded down because user debt = debt - dustDebt. rounding up would mean we reduce user debt\n (o_.dustDebtRaw.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 109);\n }\n\n // Withdrawal gap to make sure there's always liquidity for liquidation\n // For example if withdrawal allowance is 15% on liquidity then we can limit operate's withdrawal allowance to 10%\n // this will allow liquidate function to get extra 5% buffer for potential liquidations.\n if (newCol_ < 0) {\n // extracting withdrawal gap which is in 0.1% precision.\n temp_ = (o_.vaultVariables2 >> 62) & X10;\n if (temp_ > 0) {\n // fetching user's supply slot data\n o_.userSupplyLiquidityData = LIQUIDITY.readFromStorage(LIQUIDITY_USER_SUPPLY_SLOT);\n\n // converting current user's supply from big number to normal\n temp2_ = (o_.userSupplyLiquidityData >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n\n // fetching liquidity's withdrawal limit\n temp3_ = int(LiquidityCalcs.calcWithdrawalLimitBeforeOperate(o_.userSupplyLiquidityData, temp2_));\n\n // max the number could go is vault's supply * 1000. Overflowing is almost impossible.\n unchecked {\n // (liquidityUserSupply - withdrawalGap - liquidityWithdrawaLimit) should be less than user's withdrawal\n if (\n (temp3_ > 0) &&\n (((int(temp2_ * (1000 - temp_)) / 1000)) - temp3_) <\n (((-newCol_) * int(EXCHANGE_PRICES_PRECISION)) / int(o_.liquidityExPrice))\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__WithdrawMoreThanOperateLimit);\n }\n }\n }\n }\n\n {\n // execute actions at Liquidity: deposit & payback is first and then withdraw & borrow\n if (newCol_ > 0) {\n // deposit\n LIQUIDITY.operate{ value: SUPPLY_TOKEN == NATIVE_TOKEN ? msg.value : 0 }(\n SUPPLY_TOKEN,\n newCol_,\n 0,\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n }\n if (newDebt_ < 0) {\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n unchecked {\n temp_ = uint(-newDebt_);\n if (msg.value > temp_) {\n SafeTransfer.safeTransferNative(msg.sender, msg.value - temp_);\n } else if (msg.value < temp_) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n }\n } else {\n temp_ = 0;\n }\n // payback\n LIQUIDITY.operate{ value: temp_ }(\n BORROW_TOKEN,\n 0,\n newDebt_,\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n }\n if (newCol_ < 0) {\n // withdraw\n LIQUIDITY.operate(SUPPLY_TOKEN, newCol_, 0, to_, address(0), new bytes(0));\n }\n if (newDebt_ > 0) {\n // borrow\n LIQUIDITY.operate(BORROW_TOKEN, 0, newDebt_, address(0), to_, new bytes(0));\n }\n }\n\n {\n // Updating vault variables on storage\n\n // Calculating new total collateral & total debt.\n temp_ = (vaultVariables_ >> 82) & X64;\n temp_ = ((temp_ >> 8) << (temp_ & X8)) + o_.colRaw - o_.oldColRaw;\n temp2_ = (vaultVariables_ >> 146) & X64;\n temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) + (o_.debtRaw - o_.dustDebtRaw) - o_.oldNetDebtRaw;\n // Updating vault variables on storage. This will also reentrancy 0 back again\n // Converting total supply & total borrow in 64 bits (56 | 8) bignumber\n vaultVariables =\n (vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply\n (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow\n }\n\n emit LogOperate(msg.sender, nftId_, newCol_, newDebt_, to_);\n\n return (nftId_, newCol_, newDebt_);\n }\n\n /// @dev allows to liquidate all bad debt of all users at once. Liquidator can also liquidate partially any amount they want.\n /// @param debtAmt_ total debt to liquidate (aka debt token to swap into collateral token)\n /// @param colPerUnitDebt_ minimum collateral token per unit of debt in 1e18 decimals\n /// @param to_ address at which collateral token should go to.\n /// If dead address (0x000000000000000000000000000000000000dEaD) then reverts with custom error \"FluidLiquidateResult\"\n /// returning the actual collateral and actual debt liquidated. Useful to find max liquidatable amounts via try / catch.\n /// @param absorb_ if true then liquidate from absorbed first\n /// @return actualDebtAmt_ if liquidator sends debtAmt_ more than debt remaining to liquidate then actualDebtAmt_ changes from debtAmt_ else remains same\n /// @return actualColAmt_ total liquidated collateral which liquidator will get\n function liquidate(\n uint256 debtAmt_,\n uint256 colPerUnitDebt_, // min collateral needed per unit of debt in 1e18\n address to_,\n bool absorb_\n ) public payable returns (uint actualDebtAmt_, uint actualColAmt_) {\n LiquidateMemoryVars memory memoryVars_;\n\n uint vaultVariables_ = vaultVariables;\n\n // ############# turning re-entrancy bit on #############\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n if ((msg.value != debtAmt_) && (to_ != 0x000000000000000000000000000000000000dEaD)) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueLiquidate);\n }\n } else if (msg.value > 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueLiquidate);\n }\n\n memoryVars_.vaultVariables2 = vaultVariables2;\n\n if (((vaultVariables_ >> 2) & X20) == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__TopTickDoesNotExist);\n }\n\n // Below are exchange prices of vaults\n (, , memoryVars_.supplyExPrice, memoryVars_.borrowExPrice) = updateExchangePrices(memoryVars_.vaultVariables2);\n\n CurrentLiquidity memory currentData_;\n BranchData memory branch_;\n // Temporary holder variables, used many times for different small things\n uint temp_;\n uint temp2_;\n\n {\n // ############# Oracle related stuff #############\n // Col price w.r.t debt. For example: 1 ETH = 1000 DAI\n // temp_ -> debtPerCol\n temp_ = IFluidOracle(address(uint160(memoryVars_.vaultVariables2 >> 96))).getExchangeRateLiquidate(); // Price in 27 decimals\n\n // not reverting if oracle price is lower than 1e9 as it can pause potential liquidation in this edge case situations\n if (temp_ > 1e54 || temp_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOraclePrice);\n }\n\n unchecked {\n // temp_ -> debtPerCol Converting in terms of raw amount\n temp_ = (temp_ * memoryVars_.supplyExPrice) / memoryVars_.borrowExPrice;\n\n // capping oracle pricing to 1e45\n // Reason mentioned at (search: #487RGF783GF)\n if (temp_ > 1e45) {\n temp_ = 1e45;\n }\n // temp2_ -> Raw colPerDebt_ in 27 decimals\n temp2_ = 1e54 / temp_;\n\n // temp2_ can never be > 1e54\n // Oracle price should never be > 1e54\n // Liquidation penalty in 4 decimals (1e2 = 1%) (max: 10.23%) -> (vaultVariables2_ >> 72) & X10\n currentData_.colPerDebt = (temp2_ * (10000 + ((memoryVars_.vaultVariables2 >> 72) & X10))) / 10000;\n\n // get liquidiation tick (tick at liquidation threshold ratio)\n // Liquidation threshold in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 42) & X10\n // Dividing by 1e27 to convert temp_ into normal number\n temp_ = ((temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27);\n // temp2_ -> liquidationRatio_\n temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 42) & X10)) / 1000;\n }\n (memoryVars_.liquidationTick, ) = TickMath.getTickAtRatio(temp2_);\n\n // get liquidiation max limit tick (tick at liquidation max limit ratio)\n // Max limit in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 52) & X10\n // temp2_ -> maxRatio_\n unchecked {\n temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 52) & X10)) / 1000;\n }\n (memoryVars_.maxTick, ) = TickMath.getTickAtRatio(temp2_);\n }\n\n // extracting top tick as top tick will be the current tick\n unchecked {\n currentData_.tick = (vaultVariables_ & 4) == 4\n ? int256((vaultVariables_ >> 3) & X19)\n : -int256((vaultVariables_ >> 3) & X19);\n }\n\n if (currentData_.tick > memoryVars_.maxTick) {\n // absorbing all the debt above maxTick if available\n vaultVariables_ = (abi.decode(_spell(SECONDARY_IMPLEMENTATION, abi.encodeWithSignature(\"absorb(uint256,int256)\", vaultVariables_, memoryVars_.maxTick)), (uint256)));\n\n // updating current tick to new topTick after absorb\n unchecked {\n currentData_.tick = (vaultVariables_ & 4) == 4\n ? int256((vaultVariables_ >> 3) & X19)\n : -int256((vaultVariables_ >> 3) & X19);\n }\n if (debtAmt_ == 0) {\n // updating vault variables on storage as the transaction was for only absorb\n vaultVariables = vaultVariables_;\n return (0, 0);\n }\n }\n\n if (debtAmt_ < 10000 || debtAmt_ > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidationAmt);\n }\n\n // setting up status if top tick is liquidated or not\n currentData_.tickStatus = vaultVariables_ & 2 == 0 ? 1 : 2;\n // Tick info is mainly used as a place holder to store temporary tick related data\n // (it can be current or ref using same memory variable)\n TickData memory tickInfo_;\n tickInfo_.tick = currentData_.tick;\n\n {\n // ############# Setting current branch in memory #############\n\n // Updating branch related data\n branch_.id = (vaultVariables_ >> 22) & X30;\n branch_.data = branchData[branch_.id];\n branch_.debtFactor = (branch_.data >> 116) & X50;\n if (branch_.debtFactor == 0) {\n // Initializing branch debt factor. 35 | 15 bit number. Where full 35 bits and 15th bit is occupied.\n // Making the total number as (2**35 - 1) << 2**14.\n // note: initial debt factor can be any number.\n branch_.debtFactor = ((X35 << 15) | (1 << 14));\n }\n // fetching base branch's minima tick. if 0 that means it's a master branch\n temp_ = (branch_.data >> 196) & X20;\n if (temp_ > 0) {\n unchecked {\n branch_.minimaTick = (temp_ & 1) == 1 ? int256((temp_ >> 1) & X19) : -int256((temp_ >> 1) & X19);\n }\n } else {\n branch_.minimaTick = type(int).min;\n }\n }\n\n // debtAmt_ should be less than 2**128 & EXCHANGE_PRICES_PRECISION is 1e12\n unchecked {\n currentData_.debtRemaining = (debtAmt_ * EXCHANGE_PRICES_PRECISION) / memoryVars_.borrowExPrice;\n }\n\n // extracting total debt\n temp2_ = (vaultVariables_ >> 146) & X64;\n temp2_ = ((temp2_ >> 8) << (temp2_ & X8));\n\n if ((temp2_ / 1e9) > currentData_.debtRemaining) {\n // if liquidation amount is less than 1e9 of total debt then revert\n // so if total debt is $1B then minimum liquidation limit = $1\n // so if total debt is $1T then minimum liquidation limit = $1000\n // partials precision is slightlty above 1e9 so this will make sure that on every liquidation atleast 1 partial gets liquidated\n // not sure if it can result in any issue but restricting amount further more to remove very low amount scenarios totally\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidationAmt);\n }\n\n if (absorb_) {\n temp_ = absorbedLiquidity;\n // temp2_ -> absorbed col\n temp2_ = (temp_ >> 128) & X128;\n // temp_ -> absorbed debt\n temp_ = temp_ & X128;\n\n if (temp_ > currentData_.debtRemaining) {\n // Removing collateral in equal proportion as debt\n currentData_.totalColLiq = ((temp2_ * currentData_.debtRemaining) / temp_);\n temp2_ -= currentData_.totalColLiq;\n // Removing debt\n currentData_.totalDebtLiq = currentData_.debtRemaining;\n unchecked {\n temp_ -= currentData_.debtRemaining;\n }\n currentData_.debtRemaining = 0;\n\n // updating on storage\n absorbedLiquidity = temp_ | (temp2_ << 128);\n } else {\n // updating on storage\n absorbedLiquidity = 0;\n unchecked {\n currentData_.debtRemaining -= temp_;\n }\n currentData_.totalDebtLiq = temp_;\n currentData_.totalColLiq = temp2_;\n }\n }\n\n // current tick should be greater than liquidationTick and it cannot be greater than maxTick as absorb will run\n if (currentData_.tick > memoryVars_.liquidationTick) {\n if (currentData_.debtRemaining > 0) {\n // Stores liquidated debt & collateral in each loop\n uint debtLiquidated_;\n uint colLiquidated_;\n uint debtFactor_ = BigMathVault.TWO_POWER_64;\n\n TickHasDebt memory tickHasDebt_;\n unchecked {\n tickHasDebt_.mapId = (currentData_.tick < 0)\n ? (((currentData_.tick + 1) / 256) - 1)\n : (currentData_.tick / 256);\n }\n\n tickInfo_.ratio = TickMath.getRatioAtTick(tickInfo_.tick);\n\n if (currentData_.tickStatus == 1) {\n // top tick is not liquidated. Hence it's a perfect tick.\n currentData_.ratio = tickInfo_.ratio;\n // if current tick in liquidation is a perfect tick then it is also the next tick that has debt.\n tickHasDebt_.nextTick = currentData_.tick;\n } else {\n // top tick is liquidated. Hence it has partials.\n // next tick that has debt liquidity will have to be fetched from tickHasDebt\n unchecked {\n tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n tickInfo_.partials = (branch_.data >> 22) & X30;\n currentData_.ratio = tickInfo_.ratioOneLess + ((tickInfo_.length * tickInfo_.partials) / X30);\n \n if ((memoryVars_.liquidationTick + 1) == tickInfo_.tick && (tickInfo_.partials == 1)) {\n if (to_ == 0x000000000000000000000000000000000000dEaD) {\n // revert with liquidated amounts if to_ address is the dead address.\n // this can be used in a resolver to find the max liquidatable amounts.\n revert FluidLiquidateResult(0, 0);\n }\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidation);\n }\n }\n }\n\n while (true) {\n if (currentData_.tickStatus == 1) {\n // not liquidated -> Getting the debt from tick data itself\n temp2_ = tickData[currentData_.tick];\n // temp_ => tick debt\n temp_ = (temp2_ >> 25) & X64;\n // Converting big number into normal number\n temp_ = (temp_ >> 8) << (temp_ & X8);\n // Updating tickData on storage with removing debt & adding connection to branch\n tickData[currentData_.tick] =\n 1 | // set tick as liquidated\n (temp2_ & 0x1fffffe) | // set same total tick ids\n (branch_.id << 26) | // branch id where this tick got liquidated\n (branch_.debtFactor << 56);\n } else {\n // already liquidated -> Get the debt from branch data in big number\n // temp_ => tick debt\n temp_ = (branch_.data >> 52) & X64;\n // Converting big number into normal number\n temp_ = (temp_ >> 8) << (temp_ & X8);\n // Branch is getting updated over the end\n }\n\n // Adding new debt into active debt for liquidation\n currentData_.debt += temp_;\n\n // Adding new col into active col for liquidation\n // Ratio is in 2**96 decimals hence multiplying debt with 2**96 to get proper collateral\n currentData_.col += (temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / currentData_.ratio;\n\n if (\n (tickHasDebt_.nextTick == currentData_.tick && currentData_.tickStatus == 1) ||\n tickHasDebt_.tickHasDebt == 0\n ) {\n // Fetching next perfect tick with liquidity\n // tickHasDebt_.tickHasDebt == 0 will only happen in the first while loop\n // in the very first perfect tick liquidation it'll be 0\n if (tickHasDebt_.tickHasDebt == 0) {\n tickHasDebt_.tickHasDebt = tickHasDebt[tickHasDebt_.mapId];\n }\n\n // in 1st loop tickStatus can be 2. Meaning not a perfect current tick\n if (currentData_.tickStatus == 1) {\n unchecked {\n tickHasDebt_.bitsToRemove = uint(-currentData_.tick + (tickHasDebt_.mapId * 256 + 256));\n }\n // Removing current top tick from tickHasDebt\n tickHasDebt_.tickHasDebt =\n (tickHasDebt_.tickHasDebt << tickHasDebt_.bitsToRemove) >>\n tickHasDebt_.bitsToRemove;\n // Updating in storage if tickHasDebt becomes 0.\n if (tickHasDebt_.tickHasDebt == 0) {\n tickHasDebt[tickHasDebt_.mapId] = 0;\n }\n }\n\n // For last user remaining in vault there could be a lot of while loop.\n // Chances of this to happen is extremely low (like ~0%)\n while (true) {\n if (tickHasDebt_.tickHasDebt > 0) {\n unchecked {\n tickHasDebt_.nextTick =\n tickHasDebt_.mapId *\n 256 +\n int(tickHasDebt_.tickHasDebt.mostSignificantBit()) -\n 1;\n }\n break;\n }\n\n // tickHasDebt_.tickHasDebt == 0. Checking if minimum tick of this mapID is less than liquidationTick_\n // if true that means now the next tick is not needed as liquidation gets over minimum at liquidationTick_\n unchecked {\n if ((tickHasDebt_.mapId * 256) < memoryVars_.liquidationTick) {\n tickHasDebt_.nextTick = type(int).min;\n break;\n }\n\n // Fetching next tick has debt by decreasing tickHasDebt_.mapId first\n tickHasDebt_.tickHasDebt = tickHasDebt[--tickHasDebt_.mapId];\n }\n }\n }\n\n // Fetching refTick. refTick is the biggest tick of these 3:\n // 1. Next tick with liquidity (from tickHasDebt)\n // 2. Minima tick of current branch\n // 3. Liquidation threshold tick\n {\n // Setting currentData_.refTick & currentData_.refTickStatus\n if (\n branch_.minimaTick > tickHasDebt_.nextTick &&\n branch_.minimaTick > memoryVars_.liquidationTick\n ) {\n // next tick will be of base branch (merge)\n currentData_.refTick = branch_.minimaTick;\n currentData_.refTickStatus = 2;\n } else if (tickHasDebt_.nextTick > memoryVars_.liquidationTick) {\n // next tick will be next tick from perfect tick\n currentData_.refTick = tickHasDebt_.nextTick;\n currentData_.refTickStatus = 1;\n } else {\n // next tick is threshold tick\n currentData_.refTick = memoryVars_.liquidationTick;\n currentData_.refTickStatus = 3; // leads to end of liquidation loop\n }\n }\n\n // using tickInfo variable again for ref tick as we don't have the need for it any more\n tickInfo_.ratio = TickMath.getRatioAtTick(int24(currentData_.refTick));\n if (currentData_.refTickStatus == 2) {\n // merge current branch with base branch\n unchecked {\n tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n // Fetching base branch data to get the base branch's partial\n branch_.baseBranchData = branchData[((branch_.data >> 166) & X30)];\n tickInfo_.partials = (branch_.baseBranchData >> 22) & X30;\n tickInfo_.currentRatio =\n tickInfo_.ratioOneLess +\n ((tickInfo_.length * tickInfo_.partials) / X30);\n currentData_.refRatio = tickInfo_.currentRatio;\n }\n } else {\n // refTickStatus can only be 1 (next tick from perfect tick) or 3 (liquidation threshold tick)\n tickInfo_.currentRatio = tickInfo_.ratio;\n currentData_.refRatio = tickInfo_.ratio;\n tickInfo_.partials = X30;\n }\n\n // Formula: (debt_ - x) / (col_ - (x * colPerDebt_)) = ratioEnd_\n // x = ((ratioEnd_ * col) - debt_) / ((colPerDebt_ * ratioEnd_) - 1)\n // x is debtToLiquidate_\n // col_ = debt_ / ratioStart_ -> (currentData_.debt / currentData_.ratio)\n // ratioEnd_ is currentData_.refRatio\n //\n // Calculation results of numerator & denominator is always negative\n // which will cancel out to give positive output in the end so we can safely cast to uint.\n // for nominator:\n // ratioStart can only be >= ratioEnd so first part can only be reducing currentData_.debt leading to\n // currentData_.debt reduced - currentData_.debt original * 1e27 -> can only be a negative number\n // for denominator:\n // currentData_.colPerDebt and currentData_.refRatio are inversely proportional to each other.\n // the maximum value they can ever be is ~9.97e26 which is the 0.3% away from 100% because liquidation\n // threshold + liquidation penalty can never be > 99.7%. This can also be verified by going back from\n // min / max ratio values further up where we fetch oracle price etc.\n // as optimization we can inverse nominator and denominator subtraction to directly get a positive number.\n\n debtLiquidated_ =\n // nominator\n ((currentData_.debt - (currentData_.refRatio * currentData_.debt) / currentData_.ratio) *\n 1e27) /\n // denominator\n (1e27 - ((currentData_.colPerDebt * currentData_.refRatio) / TickMath.ZERO_TICK_SCALED_RATIO));\n\n colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27;\n\n if (currentData_.debt == debtLiquidated_) {\n debtLiquidated_ -= 1;\n }\n\n if (debtLiquidated_ >= currentData_.debtRemaining || currentData_.refTickStatus == 3) {\n // End of liquidation as full amount to liquidate or liquidation threshold tick has been reached;\n\n // Updating tickHasDebt on storage.\n tickHasDebt[tickHasDebt_.mapId] = tickHasDebt_.tickHasDebt;\n\n if (debtLiquidated_ >= currentData_.debtRemaining) {\n // Liquidation ended between currentTick & refTick.\n // Not all of liquidatable debt is actually liquidated -> recalculate\n debtLiquidated_ = currentData_.debtRemaining;\n colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27;\n // Liquidating to debt. temp_ => final ratio after liquidation\n // liquidatable debt - debtLiquidated / liquidatable col - colLiquidated\n temp_ =\n ((currentData_.debt - debtLiquidated_) * TickMath.ZERO_TICK_SCALED_RATIO) /\n (currentData_.col - colLiquidated_);\n // Fetching tick of where liquidation ended\n (tickInfo_.tick, tickInfo_.ratioOneLess) = TickMath.getTickAtRatio(temp_);\n if ((tickInfo_.tick < currentData_.refTick) && (tickInfo_.partials == X30)) {\n // this situation might never happen\n // if this happens then there might be some very edge case precision of few weis which is returning 1 tick less\n // if the above were to ever happen then tickInfo_.tick only be currentData_.refTick - 1\n // in this case the partial will be very very near to full (X30)\n // increasing tick by 2 and making partial as 1 which is basically very very near to currentData_.refTick\n unchecked {\n tickInfo_.tick += 2;\n }\n tickInfo_.partials = 1;\n } else {\n unchecked {\n // Increasing tick by 1 as final ratio will probably be a partial\n ++tickInfo_.tick;\n\n // if ref tick is old liquidated tick then storing partials in temp2_\n // tickInfo_.partials contains partial of branch which is the current ref tick\n temp2_ = (currentData_.refTickStatus == 2 && tickInfo_.tick == currentData_.refTick) ? tickInfo_.partials : 0;\n\n tickInfo_.ratio = (tickInfo_.ratioOneLess * 10015) / 10000;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n tickInfo_.partials = ((temp_ - tickInfo_.ratioOneLess) * X30) / tickInfo_.length;\n\n // Taking edge cases where partial comes as 0 or X30 meaning perfect tick.\n // Hence, increasing or reducing it by 1 as liquidation tick cannot be perfect tick.\n tickInfo_.partials = tickInfo_.partials == 0 ? 1 : tickInfo_.partials >= X30\n ? X30 - 1\n : tickInfo_.partials;\n }\n if (temp2_ > 0 && temp2_ >= tickInfo_.partials) {\n // if refTick is liquidated tick and hence contains partials then checking that\n // current liquidation tick's partial should not be less than last liquidation refTick\n\n // not sure if this is even possible to happen but adding checks to avoid it fully\n // if it reverts here then next liquidation on next block should go through fine\n revert FluidVaultError(ErrorTypes.VaultT1__LiquidationReverts);\n }\n }\n } else {\n // End in liquidation threshold.\n // finalRatio_ = currentData_.refRatio;\n // Increasing liquidation threshold tick by 1 partial. With 1 partial it'll reach to the next tick.\n // Ratio change will be negligible. Doing this as liquidation threshold tick can also be a perfect non-liquidated tick.\n unchecked {\n tickInfo_.tick = currentData_.refTick + 1;\n }\n // Making partial as 1 so it doesn't stay perfect tick\n tickInfo_.partials = 1;\n // length is not needed as only partials are written to storage\n }\n\n // debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt\n // -> debtFactor * leftOverDebt / liquidatableDebt\n debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt;\n currentData_.totalDebtLiq += debtLiquidated_;\n currentData_.debt -= debtLiquidated_; // currentData_.debt => leftOverDebt after debtLiquidated_\n currentData_.totalColLiq += colLiquidated_;\n currentData_.col -= colLiquidated_; // currentData_.col => leftOverCol after colLiquidated_\n\n // Updating branch's debt factor & write to storage as liquidation is over\n branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_);\n\n if (currentData_.debt < 100) {\n // this can happen when someone tries to create a dust tick\n revert FluidVaultError(ErrorTypes.VaultT1__BranchDebtTooLow);\n }\n\n unchecked {\n // Tick to insert\n temp2_ = tickInfo_.tick < 0\n ? (uint(-tickInfo_.tick) << 1)\n : ((uint(tickInfo_.tick) << 1) | 1);\n }\n\n // Updating Branch data with debt factor, debt, partials, minima tick & assigning is liquidated\n branchData[branch_.id] =\n ((branch_.data >> 166) << 166) |\n 1 | // set as liquidated\n (temp2_ << 2) | // minima tick of branch\n (tickInfo_.partials << 22) |\n (currentData_.debt.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52) | // branch debt\n (branch_.debtFactor << 116);\n\n // Updating vault variables with current branch & tick\n vaultVariables_ =\n ((vaultVariables_ >> 52) << 52) |\n 2 | // set as liquidated\n (temp2_ << 2) | // top tick\n (branch_.id << 22);\n break;\n }\n\n unchecked {\n // debtLiquidated_ >= currentData_.debtRemaining leads to loop break in if statement above\n // so this can be unchecked\n currentData_.debtRemaining -= debtLiquidated_;\n }\n\n // debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt\n // -> debtFactor * leftOverDebt / liquidatableDebt\n debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt;\n currentData_.totalDebtLiq += debtLiquidated_;\n currentData_.debt -= debtLiquidated_;\n currentData_.totalColLiq += colLiquidated_;\n currentData_.col -= colLiquidated_;\n\n // updating branch's debt factor\n branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_);\n // Setting debt factor as 1 << 64 again\n debtFactor_ = BigMathVault.TWO_POWER_64;\n\n if (currentData_.refTickStatus == 2) {\n // ref tick is base branch's minima hence merging current branch to base branch\n // and making base branch as current branch.\n\n // read base branch related data\n temp_ = (branch_.data >> 166) & X30; // temp_ -> base branch id\n temp2_ = branch_.baseBranchData;\n {\n uint newBranchDebtFactor_ = (temp2_ >> 116) & X50;\n\n // connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor\n uint connectionFactor_ = newBranchDebtFactor_.divBigNumber(branch_.debtFactor);\n // Updating current branch in storage\n branchData[branch_.id] =\n ((branch_.data >> 166) << 166) | // deleting debt / partials / minima tick\n 2 | // setting as merged\n (connectionFactor_ << 116); // set new connectionFactor\n\n // Storing base branch in memory\n // Updating branch ID to base branch ID\n branch_.id = temp_;\n // Updating branch data with base branch data\n branch_.data = temp2_;\n // Remove next branch connection from base branch\n branch_.debtFactor = newBranchDebtFactor_;\n // temp_ => minima tick of base branch\n temp_ = (temp2_ >> 196) & X20;\n if (temp_ > 0) {\n unchecked {\n branch_.minimaTick = (temp_ & 1) == 1\n ? int256((temp_ >> 1) & X19)\n : -int256((temp_ >> 1) & X19);\n }\n } else {\n branch_.minimaTick = type(int).min;\n }\n }\n }\n\n // Making refTick as currentTick\n currentData_.tick = currentData_.refTick;\n currentData_.tickStatus = currentData_.refTickStatus;\n currentData_.ratio = currentData_.refRatio;\n }\n }\n }\n\n // calculating net token amounts using exchange price\n actualDebtAmt_ = (currentData_.totalDebtLiq * memoryVars_.borrowExPrice) / EXCHANGE_PRICES_PRECISION;\n actualColAmt_ = (currentData_.totalColLiq * memoryVars_.supplyExPrice) / EXCHANGE_PRICES_PRECISION;\n\n // Chances of this to happen are in few wei\n if (actualDebtAmt_ > debtAmt_) {\n // calc new actualColAmt_ via ratio.\n actualColAmt_ = actualColAmt_ * (debtAmt_ / actualDebtAmt_);\n actualDebtAmt_ = debtAmt_;\n }\n\n if (actualDebtAmt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidation);\n }\n\n if (((actualColAmt_ * 1e18) / actualDebtAmt_) < colPerUnitDebt_) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessSlippageLiquidation);\n }\n\n if (to_ == 0x000000000000000000000000000000000000dEaD) {\n // revert with liquidated amounts if to_ address is the dead address.\n // this can be used in a resolver to find the max liquidatable amounts.\n revert FluidLiquidateResult(actualColAmt_, actualDebtAmt_);\n }\n\n // payback at Liquidity\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n temp_ = actualDebtAmt_;\n if (actualDebtAmt_ < msg.value) {\n unchecked {\n // subtraction can be unchecked because of if check above\n SafeTransfer.safeTransferNative(msg.sender, msg.value - actualDebtAmt_);\n }\n }\n // else if actualDebtAmt_ > msg.value not possible as actualDebtAmt_ can maximally be debtAmt_ and\n // msg.value == debtAmt_ is checked in the beginning of function.\n } else {\n temp_ = 0;\n }\n unchecked {\n // payback at liquidity\n LIQUIDITY.operate{ value: temp_ }(\n BORROW_TOKEN,\n 0,\n -int(actualDebtAmt_),\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n // withdraw at liquidity\n LIQUIDITY.operate(SUPPLY_TOKEN, -int(actualColAmt_), 0, to_, address(0), new bytes(0));\n }\n\n // Calculating new total collateral & total debt.\n // temp_ -> total supply\n temp_ = (vaultVariables_ >> 82) & X64;\n temp_ = ((temp_ >> 8) << (temp_ & X8)) - currentData_.totalColLiq;\n // temp2_ -> total borrow\n temp2_ = (vaultVariables_ >> 146) & X64;\n temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) - currentData_.totalDebtLiq;\n // Updating vault variables on storage\n // Converting total supply & total borrow in 64 bits (56 | 8) bignumber\n vaultVariables =\n (vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply\n (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow\n\n emit LogLiquidate(msg.sender, actualColAmt_, actualDebtAmt_, to_);\n }\n\n /// @dev Checks total supply of vault's in Liquidity Layer & Vault contract and rebalance it accordingly\n /// if vault supply is more than Liquidity Layer then deposit difference through reserve/rebalance contract\n /// if vault supply is less than Liquidity Layer then withdraw difference to reserve/rebalance contract\n /// if vault borrow is more than Liquidity Layer then borrow difference to reserve/rebalance contract\n /// if vault borrow is less than Liquidity Layer then payback difference through reserve/rebalance contract\n function rebalance() external payable returns (int supplyAmt_, int borrowAmt_) {\n (supplyAmt_, borrowAmt_) = abi.decode(_spell(SECONDARY_IMPLEMENTATION, msg.data), (int, int));\n }\n\n /// @dev liquidity callback for cheaper token transfers in case of deposit or payback.\n /// only callable by Liquidity during an operation.\n function liquidityCallback(address token_, uint amount_, bytes calldata data_) external {\n if (msg.sender != address(LIQUIDITY))\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidityCallbackAddress);\n if (vaultVariables & 1 == 0) revert FluidVaultError(ErrorTypes.VaultT1__NotEntered);\n\n SafeTransfer.safeTransferFrom(token_, abi.decode(data_, (address)), address(LIQUIDITY), amount_);\n }\n\n constructor(ConstantViews memory constants_) Helpers(constants_) {\n // Note that vaults are deployed by VaultFactory so we somewhat trust the values being passed in\n\n // Setting branch in vault.\n vaultVariables = (vaultVariables) | (1 << 22) | (1 << 52);\n\n uint liqSupplyExchangePrice_ = (LIQUIDITY.readFromStorage(LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT) >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64;\n uint liqBorrowExchangePrice_ = (LIQUIDITY.readFromStorage(LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT) >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64;\n\n if (\n liqSupplyExchangePrice_ < EXCHANGE_PRICES_PRECISION || liqBorrowExchangePrice_ < EXCHANGE_PRICES_PRECISION\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__TokenNotInitialized);\n }\n // Updating initial rates in storage\n rates =\n liqSupplyExchangePrice_ |\n (liqBorrowExchangePrice_ << 64) |\n (EXCHANGE_PRICES_PRECISION << 128) |\n (EXCHANGE_PRICES_PRECISION << 192);\n }\n\n fallback() external {\n if (!(VAULT_FACTORY.isGlobalAuth(msg.sender) || VAULT_FACTORY.isVaultAuth(address(this), msg.sender))) {\n revert FluidVaultError(ErrorTypes.VaultT1__NotAnAuth);\n }\n\n // Delegate the current call to `implementation`.\n // This does not return to its internall call site, it will return directly to the external caller.\n // solhint-disable-next-line no-inline-assembly\n _spell(ADMIN_IMPLEMENTATION, msg.data);\n }\n\n function _spell(address target_, bytes memory data_) private returns (bytes memory response_) {\n assembly {\n let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)\n let size := returndatasize()\n\n response_ := mload(0x40)\n mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))\n mstore(response_, size)\n returndatacopy(add(response_, 0x20), 0, size)\n\n switch iszero(succeeded)\n case 1 {\n // throw if delegatecall failed\n returndatacopy(0x00, 0x00, size)\n revert(0x00, size)\n }\n }\n }\n}\n"
|
||
},
|
||
"contracts/protocols/vault/vaultT1/coreModule/main2.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"../common/variables.sol\";\nimport { IFluidOracle } from \"../../../../oracle/fluidOracle.sol\";\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { Error } from \"../../error.sol\";\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { IFluidVaultT1 } from \"../../interfaces/iVaultT1.sol\";\nimport { Structs } from \"./structs.sol\";\nimport { Events } from \"./events.sol\";\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\nimport { LiquidityCalcs } from \"../../../../libraries/liquidityCalcs.sol\";\nimport { IFluidLiquidity } from \"../../../../liquidity/interfaces/iLiquidity.sol\";\nimport { SafeTransfer } from \"../../../../libraries/safeTransfer.sol\";\n\n/// @notice Fluid Vault protocol secondary methods contract.\n/// Implements `absorb()` and `rebalance()` methods, extracted from main contract due to contract size limits.\n/// Methods are limited to be called via delegateCall only (as done by Vault CoreModule \"VaultT1\" contract).\ncontract FluidVaultT1Secondary is Variables, Error, Structs, Events {\n using BigMathMinified for uint;\n\n address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // 30 bits (used for partials mainly)\n uint internal constant X8 = 0xff;\n uint internal constant X10 = 0x3ff;\n uint internal constant X16 = 0xffff;\n uint internal constant X19 = 0x7ffff;\n uint internal constant X20 = 0xfffff;\n uint internal constant X24 = 0xffffff;\n uint internal constant X25 = 0x1ffffff;\n uint internal constant X30 = 0x3fffffff;\n uint internal constant X35 = 0x7ffffffff;\n uint internal constant X50 = 0x3ffffffffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n uint internal constant X96 = 0xffffffffffffffffffffffff;\n uint internal constant X128 = 0xffffffffffffffffffffffffffffffff;\n\n address private immutable addressThis;\n\n constructor() {\n addressThis = address(this);\n }\n\n modifier _verifyCaller() {\n if (address(this) == addressThis) {\n revert FluidVaultError(ErrorTypes.VaultT1__OnlyDelegateCallAllowed);\n }\n _;\n }\n\n /// @dev absorb function absorbs the bad debt if the bad debt is above max limit. The main use of it is\n /// if the bad debt didn't got liquidated in time maybe due to sudden price drop or bad debt was extremely small to liquidate\n /// and the bad debt goes above 100% ratio then there's no incentive for anyone to liquidate now\n /// hence absorb functions absorbs that bad debt to allow newer bad debt to liquidate seamlessly.\n /// if absorbing were to happen after this it's on governance on how to deal with it\n /// although it can still be removed through liquidate via liquidator if the price goes back up and liquidation becomes beneficial\n /// upon absorbed user position gets 100% liquidated.\n function absorb(uint vaultVariables_, int maxTick_) public _verifyCaller returns (uint) {\n AbsorbMemoryVariables memory a_;\n\n // Temporary holder variables, used many times for different small few liner things\n uint temp_;\n uint temp2_;\n\n TickHasDebt memory tickHasDebt_;\n\n {\n // liquidating ticks above max ratio\n\n // temp_ -> top tick\n temp_ = ((vaultVariables_ >> 2) & X20);\n // increasing startingTick_ by 1 so the current tick comes into looping equation\n a_.startingTick = (temp_ & 1) == 1 ? (int(temp_ >> 1) + 1) : (-int(temp_ >> 1) + 1);\n\n tickHasDebt_.mapId = a_.startingTick < 0 ? ((a_.startingTick + 1) / 256) - 1 : a_.startingTick / 256;\n\n tickHasDebt_.tickHasDebt = tickHasDebt[tickHasDebt_.mapId];\n\n {\n // For last user remaining in vault there could be a lot of while loop.\n // Chances of this to happen is extremely low (like ~0%)\n tickHasDebt_.nextTick = TickMath.MAX_TICK;\n while (true) {\n if (tickHasDebt_.tickHasDebt > 0) {\n a_.mostSigBit = tickHasDebt_.tickHasDebt.mostSignificantBit();\n tickHasDebt_.nextTick = tickHasDebt_.mapId * 256 + int(a_.mostSigBit) - 1;\n\n while (tickHasDebt_.nextTick > maxTick_) {\n // storing tickData into temp_\n temp_ = tickData[tickHasDebt_.nextTick];\n // temp2_ -> tick's debt\n temp2_ = (temp_ >> 25) & X64;\n // converting big number into normal number\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n // Absorbing tick's debt & collateral\n a_.debtAbsorbed += temp2_;\n // calculating collateral from debt & ratio and adding to a_.colAbsorbed\n a_.colAbsorbed += ((temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) /\n TickMath.getRatioAtTick(int24(tickHasDebt_.nextTick)));\n // Update tick data on storage. Making tick as 100% liquidated\n tickData[tickHasDebt_.nextTick] = 1 | (temp_ & 0x1fffffe) | (1 << 25); // set as 100% liquidated\n\n // temp_ = bits to remove\n temp_ = 257 - a_.mostSigBit;\n tickHasDebt_.tickHasDebt = (tickHasDebt_.tickHasDebt << temp_) >> temp_;\n if (tickHasDebt_.tickHasDebt == 0) break;\n\n a_.mostSigBit = tickHasDebt_.tickHasDebt.mostSignificantBit();\n tickHasDebt_.nextTick = tickHasDebt_.mapId * 256 + int(a_.mostSigBit) - 1;\n }\n // updating tickHasDebt on storage\n tickHasDebt[tickHasDebt_.mapId] = tickHasDebt_.tickHasDebt;\n }\n\n // tickHasDebt_.tickHasDebt == 0 from here.\n\n if (tickHasDebt_.nextTick <= maxTick_) {\n break;\n }\n\n if (tickHasDebt_.mapId < -129) {\n tickHasDebt_.nextTick = type(int).min;\n break;\n }\n\n // Fetching next tickHasDebt by decreasing tickHasDebt_.mapId first\n tickHasDebt_.tickHasDebt = tickHasDebt[--tickHasDebt_.mapId];\n }\n }\n }\n\n // After the above loop we will get nextTick stored in tickHasDebt_ which we will use to compare & set things in the end\n\n {\n TickData memory tickInfo_;\n BranchData memory branch_;\n // if this remains 0 that means create a new branch over the end\n uint newBranchId_;\n\n {\n // Liquidate branches in a loop and store the end branch\n branch_.id = (vaultVariables_ >> 22) & X30;\n branch_.data = branchData[branch_.id];\n // Checking if current branch is liquidated\n if ((vaultVariables_ & 2) == 0) {\n // current branch is not liquidated hence it can be used as a new branch if needed\n newBranchId_ = branch_.id;\n\n // Checking the base branch minima tick. temp_ = base branch minima tick\n temp_ = (branch_.data >> 196) & X20;\n if (temp_ > 0) {\n // Setting the base branch as current liquidatable branch\n branch_.id = (branch_.data >> 166) & X30;\n branch_.data = branchData[branch_.id];\n branch_.minimaTick = (temp_ & 1) == 1 ? int(temp_ >> 1) : -int(temp_ >> 1);\n } else {\n // the current branch is base branch, hence need to setup a new base branch\n branch_.id = 0;\n branch_.data = 0;\n branch_.minimaTick = type(int).min;\n }\n } else {\n // current branch is liquidated\n temp_ = (branch_.data >> 2) & X20;\n branch_.minimaTick = (temp_ & 1) == 1 ? int(temp_ >> 1) : -int(temp_ >> 1);\n }\n while (branch_.minimaTick > maxTick_) {\n // Check base branch, if exists then check if minima tick is above max tick then liquidate it.\n tickInfo_.ratio = TickMath.getRatioAtTick(int24(branch_.minimaTick));\n tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n\n // partials\n tickInfo_.partials = (branch_.data >> 22) & X30;\n\n tickInfo_.currentRatio = tickInfo_.ratioOneLess + ((tickInfo_.length * tickInfo_.partials) / X30);\n\n // debt in branch\n temp2_ = (branch_.data >> 52) & X64;\n // converting big number into normal number\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n // Absorbing branch's debt & collateral\n a_.debtAbsorbed += temp2_;\n // calculating branch's collateral using debt & ratio and adding it to a_.colAbsorbed\n a_.colAbsorbed += (temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) / tickInfo_.currentRatio;\n\n // Closing branch\n branchData[branch_.id] = branch_.data | 3;\n\n // Setting new branch\n temp_ = (branch_.data >> 196) & X20; // temp_ -> minima tick of connected branch\n if (temp_ > 0) {\n // Setting the base branch as current liquidatable branch\n branch_.id = (branch_.data >> 166) & X30;\n branch_.data = branchData[branch_.id];\n branch_.minimaTick = (temp_ & 1) == 1 ? int(temp_ >> 1) : -int(temp_ >> 1);\n } else {\n // the current branch is base branch, hence need to setup a new base branch\n branch_.id = 0;\n branch_.data = 0;\n branch_.minimaTick = type(int).min;\n }\n }\n }\n\n if (tickHasDebt_.nextTick >= branch_.minimaTick) {\n // new top tick is not liquidated\n // temp2_ = tick to insert\n if (tickHasDebt_.nextTick > type(int).min) {\n temp2_ = tickHasDebt_.nextTick < 0\n ? (uint(-tickHasDebt_.nextTick) << 1)\n : ((uint(tickHasDebt_.nextTick) << 1) | 1);\n } else {\n temp2_ = 0;\n }\n if (newBranchId_ == 0) {\n // initializing a new branch\n // newBranchId_ = total current branches + 1\n unchecked {\n newBranchId_ = ((vaultVariables_ >> 52) & X30) + 1;\n }\n vaultVariables_ =\n ((vaultVariables_ >> 82) << 82) |\n (temp2_ << 2) |\n (newBranchId_ << 22) |\n (newBranchId_ << 52);\n } else {\n // using already initialized non liquidated branch\n vaultVariables_ = ((vaultVariables_ >> 22) << 22) | (temp2_ << 2);\n }\n\n if (branch_.minimaTick > type(int).min) {\n temp2_ = branch_.minimaTick < 0\n ? (uint(-branch_.minimaTick) << 1)\n : ((uint(branch_.minimaTick) << 1) | 1);\n // set base branch id and minima tick\n branchData[newBranchId_] = (branch_.id << 166) | (temp2_ << 196);\n } else {\n // new base branch does not have any connected branch\n branchData[newBranchId_] = 0;\n }\n } else {\n // new top tick is liquidated\n temp2_ = branch_.minimaTick < 0\n ? (uint(-branch_.minimaTick) << 1)\n : ((uint(branch_.minimaTick) << 1) | 1);\n if (newBranchId_ == 0) {\n vaultVariables_ = ((vaultVariables_ >> 52) << 52) | 2 | (temp2_ << 2) | (branch_.id << 22);\n } else {\n // uninitializing the non liquidated branch\n vaultVariables_ =\n ((vaultVariables_ >> 82) << 82) |\n 2 |\n (temp2_ << 2) |\n (branch_.id << 22) |\n ((newBranchId_ - 1) << 52); // decreasing total branch by 1\n branchData[newBranchId_] = 0;\n }\n }\n }\n\n // updating absorbed liquidity on storage\n absorbedLiquidity = absorbedLiquidity + a_.debtAbsorbed + (a_.colAbsorbed << 128);\n\n emit LogAbsorb(a_.colAbsorbed, a_.debtAbsorbed);\n\n // returning updated vault variables\n return vaultVariables_;\n }\n\n /// @dev Checks total supply of vault's in Liquidity Layer & Vault contract and rebalance it accordingly\n /// if vault supply is more than Liquidity Layer then deposit difference through reserve/rebalance contract\n /// if vault supply is less than Liquidity Layer then withdraw difference to reserve/rebalance contract\n /// if vault borrow is more than Liquidity Layer then borrow difference to reserve/rebalance contract\n /// if vault borrow is less than Liquidity Layer then payback difference through reserve/rebalance contract\n function rebalance() external payable _verifyCaller returns (int supplyAmt_, int borrowAmt_) {\n if (msg.sender != rebalancer) {\n revert FluidVaultError(ErrorTypes.VaultT1__NotRebalancer);\n }\n\n uint vaultVariables_ = vaultVariables;\n // ############# turning re-entrancy bit on #############\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n IFluidVaultT1.ConstantViews memory constants_ = IFluidVaultT1(address(this)).constantsView();\n\n if (msg.value > 0 && !(constants_.supplyToken == NATIVE_TOKEN || constants_.borrowToken == NATIVE_TOKEN)) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueInRebalance);\n }\n\n IFluidLiquidity liquidity_ = IFluidLiquidity(constants_.liquidity);\n RebalanceMemoryVariables memory r_;\n\n (r_.liqSupplyExPrice, r_.liqBorrowExPrice, r_.vaultSupplyExPrice, r_.vaultBorrowExPrice) = IFluidVaultT1(\n address(this)\n ).updateExchangePrices(vaultVariables2);\n\n // extract vault supply at Liquidity -> 64 bits starting from bit 1 (first bit is interest mode)\n uint totalSupplyLiquidity_ = (liquidity_.readFromStorage(constants_.liquidityUserSupplySlot) >>\n LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n totalSupplyLiquidity_ = (totalSupplyLiquidity_ >> 8) << (totalSupplyLiquidity_ & X8);\n totalSupplyLiquidity_ =\n (totalSupplyLiquidity_ * r_.liqSupplyExPrice) /\n LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n\n // extract vault borrowings at Liquidity -> 64 bits starting from bit 1 (first bit is interest mode)\n uint totalBorrowLiquidity_ = (liquidity_.readFromStorage(constants_.liquidityUserBorrowSlot) >>\n LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64;\n totalBorrowLiquidity_ = (totalBorrowLiquidity_ >> 8) << (totalBorrowLiquidity_ & X8);\n totalBorrowLiquidity_ =\n (totalBorrowLiquidity_ * r_.liqBorrowExPrice) /\n LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n\n uint totalSupplyVault_ = (vaultVariables_ >> 82) & X64;\n totalSupplyVault_ = (totalSupplyVault_ >> 8) << (totalSupplyVault_ & X8);\n totalSupplyVault_ = (totalSupplyVault_ * r_.vaultSupplyExPrice) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n\n uint totalBorrowVault_ = (vaultVariables_ >> 146) & X64;\n totalBorrowVault_ = (totalBorrowVault_ >> 8) << (totalBorrowVault_ & X8);\n totalBorrowVault_ = (totalBorrowVault_ * r_.vaultBorrowExPrice) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION;\n\n uint value_;\n\n if (totalSupplyVault_ > totalSupplyLiquidity_) {\n // Fetch tokens from revenue/rebalance contract and supply in liquidity contract\n // This is the scenario when the supply rewards are going in vault, hence\n // the vault total supply is increasing at a higher pace than Liquidity contract.\n // We are not transferring rewards right when we set the rewards to keep things clean.\n // Also, this can also happen in case when supply rate magnifier is greater than 1.\n\n supplyAmt_ = int(totalSupplyVault_) - int(totalSupplyLiquidity_);\n\n if (constants_.supplyToken == NATIVE_TOKEN) {\n if (msg.value > uint(supplyAmt_)) {\n value_ = uint(supplyAmt_);\n SafeTransfer.safeTransferNative(msg.sender, msg.value - value_); // sending back excess ETH\n } else {\n value_ = msg.value; // setting amount as msg.value\n }\n supplyAmt_ = int(value_);\n } else {\n value_ = 0;\n }\n\n try liquidity_.operate{ value: value_ }(\n constants_.supplyToken,\n supplyAmt_,\n 0,\n address(0),\n address(0),\n abi.encode(rebalancer)\n ) {\n // if success then do nothing\n } catch {\n supplyAmt_ = 0;\n }\n\n \n } else if (totalSupplyLiquidity_ > totalSupplyVault_) {\n if (constants_.supplyToken == NATIVE_TOKEN && msg.value > 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueInRebalance);\n }\n // Withdraw from Liquidity contract and send it to revenue contract.\n // This is the scenario when the vault user's are getting less ETH APR than what's going on Liquidity contract.\n // When supply rate magnifier is less than 1.\n supplyAmt_ = int(totalSupplyVault_) - int(totalSupplyLiquidity_);\n try liquidity_.operate(constants_.supplyToken, supplyAmt_, 0, rebalancer, address(0), new bytes(0)) {\n // if success then do nothing\n } catch {\n supplyAmt_ = 0;\n }\n }\n\n if (totalBorrowVault_ > totalBorrowLiquidity_) {\n if (constants_.borrowToken == NATIVE_TOKEN && msg.value > 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueInRebalance);\n }\n // Borrow from Liquidity contract and send to revenue/rebalance contract\n // This is the scenario when the vault is charging more borrow to user than the Liquidity contract.\n // When borrow rate magnifier is greater than 1.\n borrowAmt_ = int(totalBorrowVault_) - int(totalBorrowLiquidity_);\n try liquidity_.operate(constants_.borrowToken, 0, borrowAmt_, address(0), rebalancer, new bytes(0)) {\n // if success then do nothing\n } catch {\n borrowAmt_ = 0;\n }\n } else if (totalBorrowLiquidity_ > totalBorrowVault_) {\n // Transfer from revenue/rebalance contract and payback on Liquidity contract\n // This is the scenario when vault protocol is earning rewards so effective borrow rate for users is low.\n // Or the case where borrow rate magnifier is less than 1\n\n borrowAmt_ = int(totalBorrowLiquidity_) - int(totalBorrowVault_);\n\n if (constants_.borrowToken == NATIVE_TOKEN) {\n if (msg.value > uint(borrowAmt_)) {\n value_ = uint(borrowAmt_);\n SafeTransfer.safeTransferNative(msg.sender, msg.value - value_);\n } else {\n value_ = msg.value; // setting amount as msg.value\n }\n borrowAmt_ = int(value_);\n } else {\n value_ = 0;\n }\n\n borrowAmt_ = -borrowAmt_;\n\n try liquidity_.operate{ value: value_ }(\n constants_.borrowToken,\n 0,\n borrowAmt_,\n address(0),\n address(0),\n abi.encode(rebalancer)\n ) {\n // if success then do nothing\n } catch {\n borrowAmt_ = 0;\n }\n }\n\n if (supplyAmt_ == 0 && borrowAmt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__NothingToRebalance);\n }\n\n // Updating vault variable on storage to turn off the reentrancy bit\n vaultVariables = vaultVariables_;\n\n emit LogRebalance(supplyAmt_, borrowAmt_);\n }\n}\n"
|
||
},
|
||
"contracts/protocols/vault/vaultT1/coreModule/structs.sol": {
|
||
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Structs {\n // structs are used to mitigate Stack too deep errors\n\n struct OperateMemoryVars {\n // ## User's position before update ##\n uint oldColRaw;\n uint oldNetDebtRaw; // total debt - dust debt\n int oldTick;\n // ## User's position after update ##\n uint colRaw;\n uint debtRaw;\n uint dustDebtRaw;\n int tick;\n uint tickId;\n // others\n uint256 vaultVariables2;\n uint256 branchId;\n int256 topTick;\n uint liquidityExPrice;\n uint supplyExPrice;\n uint borrowExPrice;\n uint branchData;\n // user's supply slot data in liquidity\n uint userSupplyLiquidityData;\n }\n\n struct BranchData {\n uint id;\n uint data;\n uint ratio;\n uint debtFactor;\n int minimaTick;\n uint baseBranchData;\n }\n\n struct TickData {\n int tick;\n uint data;\n uint ratio;\n uint ratioOneLess;\n uint length;\n uint currentRatio; // current tick is ratio with partials.\n uint partials;\n }\n\n // note: All the below token amounts are in raw form.\n struct CurrentLiquidity {\n uint256 debtRemaining; // Debt remaining to liquidate\n uint256 debt; // Current liquidatable debt before reaching next check point\n uint256 col; // Calculate using debt & ratioCurrent\n uint256 colPerDebt; // How much collateral to liquidate per unit of Debt\n uint256 totalDebtLiq; // Total debt liquidated till now\n uint256 totalColLiq; // Total collateral liquidated till now\n int tick; // Current tick to liquidate\n uint ratio; // Current ratio to liquidate\n uint tickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick\n int refTick; // ref tick to liquidate\n uint refRatio; // ratio at ref tick\n uint refTickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick, if 3 that means it's a liquidation threshold\n }\n\n struct TickHasDebt {\n int tick; // current tick\n int nextTick; // next tick with liquidity\n int mapId; // mapping ID of tickHasDebt\n uint bitsToRemove; // liquidity to remove till tick_ so we can search for next tick\n uint tickHasDebt; // getting tickHasDebt_ from tickHasDebt[mapId_]\n uint mostSigBit; // most significant bit in tickHasDebt_ to get the next tick\n }\n\n struct LiquidateMemoryVars {\n uint256 vaultVariables2;\n int liquidationTick;\n int maxTick;\n uint256 supplyExPrice;\n uint256 borrowExPrice;\n }\n\n struct AbsorbMemoryVariables {\n uint256 debtAbsorbed;\n uint256 colAbsorbed;\n int256 startingTick;\n uint256 mostSigBit;\n }\n\n struct ConstantViews {\n address liquidity;\n address factory;\n address adminImplementation;\n address secondaryImplementation;\n address supplyToken;\n address borrowToken;\n uint8 supplyDecimals;\n uint8 borrowDecimals;\n uint vaultId;\n bytes32 liquiditySupplyExchangePriceSlot;\n bytes32 liquidityBorrowExchangePriceSlot;\n bytes32 liquidityUserSupplySlot;\n bytes32 liquidityUserBorrowSlot;\n }\n\n struct RebalanceMemoryVariables {\n uint256 liqSupplyExPrice;\n uint256 liqBorrowExPrice;\n uint256 vaultSupplyExPrice;\n uint256 vaultBorrowExPrice;\n }\n}\n"
|
||
},
|
||
"solmate/src/auth/Owned.sol": {
|
||
"content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)\nabstract contract Owned {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event OwnershipTransferred(address indexed user, address indexed newOwner);\n\n /*//////////////////////////////////////////////////////////////\n OWNERSHIP STORAGE\n //////////////////////////////////////////////////////////////*/\n\n address public owner;\n\n modifier onlyOwner() virtual {\n require(msg.sender == owner, \"UNAUTHORIZED\");\n\n _;\n }\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(address _owner) {\n owner = _owner;\n\n emit OwnershipTransferred(address(0), _owner);\n }\n\n /*//////////////////////////////////////////////////////////////\n OWNERSHIP LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function transferOwnership(address newOwner) public virtual onlyOwner {\n owner = newOwner;\n\n emit OwnershipTransferred(msg.sender, newOwner);\n }\n}\n"
|
||
},
|
||
"solmate/src/utils/FixedPointMathLib.sol": {
|
||
"content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Arithmetic library with operations for fixed-point numbers.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)\n/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)\nlibrary FixedPointMathLib {\n /*//////////////////////////////////////////////////////////////\n SIMPLIFIED FIXED POINT OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n uint256 internal constant MAX_UINT256 = 2**256 - 1;\n\n uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.\n\n function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.\n }\n\n function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.\n }\n\n function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.\n }\n\n function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.\n }\n\n /*//////////////////////////////////////////////////////////////\n LOW LEVEL FIXED POINT OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function mulDivDown(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))\n if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {\n revert(0, 0)\n }\n\n // Divide x * y by the denominator.\n z := div(mul(x, y), denominator)\n }\n }\n\n function mulDivUp(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))\n if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {\n revert(0, 0)\n }\n\n // If x * y modulo the denominator is strictly greater than 0,\n // 1 is added to round up the division of x * y by the denominator.\n z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))\n }\n }\n\n function rpow(\n uint256 x,\n uint256 n,\n uint256 scalar\n ) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n switch x\n case 0 {\n switch n\n case 0 {\n // 0 ** 0 = 1\n z := scalar\n }\n default {\n // 0 ** n = 0\n z := 0\n }\n }\n default {\n switch mod(n, 2)\n case 0 {\n // If n is even, store scalar in z for now.\n z := scalar\n }\n default {\n // If n is odd, store x in z for now.\n z := x\n }\n\n // Shifting right by 1 is like dividing by 2.\n let half := shr(1, scalar)\n\n for {\n // Shift n right by 1 before looping to halve it.\n n := shr(1, n)\n } n {\n // Shift n right by 1 each iteration to halve it.\n n := shr(1, n)\n } {\n // Revert immediately if x ** 2 would overflow.\n // Equivalent to iszero(eq(div(xx, x), x)) here.\n if shr(128, x) {\n revert(0, 0)\n }\n\n // Store x squared.\n let xx := mul(x, x)\n\n // Round to the nearest number.\n let xxRound := add(xx, half)\n\n // Revert if xx + half overflowed.\n if lt(xxRound, xx) {\n revert(0, 0)\n }\n\n // Set x to scaled xxRound.\n x := div(xxRound, scalar)\n\n // If n is even:\n if mod(n, 2) {\n // Compute z * x.\n let zx := mul(z, x)\n\n // If z * x overflowed:\n if iszero(eq(div(zx, x), z)) {\n // Revert if x is non-zero.\n if iszero(iszero(x)) {\n revert(0, 0)\n }\n }\n\n // Round to the nearest number.\n let zxRound := add(zx, half)\n\n // Revert if zx + half overflowed.\n if lt(zxRound, zx) {\n revert(0, 0)\n }\n\n // Return properly scaled zxRound.\n z := div(zxRound, scalar)\n }\n }\n }\n }\n }\n\n /*//////////////////////////////////////////////////////////////\n GENERAL NUMBER UTILITIES\n //////////////////////////////////////////////////////////////*/\n\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n let y := x // We start y at x, which will help us make our initial estimate.\n\n z := 181 // The \"correct\" value is 1, but this saves a multiplication later.\n\n // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad\n // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.\n\n // We check y >= 2^(k + 8) but shift right by k bits\n // each branch to ensure that if x >= 256, then y >= 256.\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n // Goal was to get z*z*y within a small factor of x. More iterations could\n // get y in a tighter range. Currently, we will have y in [256, 256*2^16).\n // We ensured y >= 256 so that the relative difference between y and y+1 is small.\n // That's not possible if x < 256 but we can just verify those cases exhaustively.\n\n // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.\n // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.\n // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.\n\n // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range\n // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.\n\n // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate\n // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.\n\n // There is no overflow risk here since y < 2^136 after the first branch above.\n z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.\n\n // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n // If x+1 is a perfect square, the Babylonian method cycles between\n // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.\n // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division\n // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.\n // If you don't care whether the floor or ceil square root is returned, you can remove this statement.\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n // Mod x by y. Note this will return\n // 0 instead of reverting if y is zero.\n z := mod(x, y)\n }\n }\n\n function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {\n /// @solidity memory-safe-assembly\n assembly {\n // Divide x by y. Note this will return\n // 0 instead of reverting if y is zero.\n r := div(x, y)\n }\n }\n\n function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {\n /// @solidity memory-safe-assembly\n assembly {\n // Add 1 to x * y if x % y > 0. Note this will\n // return 0 instead of reverting if y is zero.\n z := add(gt(mod(x, y), 0), div(x, y))\n }\n }\n}\n"
|
||
},
|
||
"solmate/src/utils/SSTORE2.sol": {
|
||
"content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Read and write to persistent storage at a fraction of the cost.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)\n/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)\nlibrary SSTORE2 {\n uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.\n\n /*//////////////////////////////////////////////////////////////\n WRITE LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function write(bytes memory data) internal returns (address pointer) {\n // Prefix the bytecode with a STOP opcode to ensure it cannot be called.\n bytes memory runtimeCode = abi.encodePacked(hex\"00\", data);\n\n bytes memory creationCode = abi.encodePacked(\n //---------------------------------------------------------------------------------------------------------------//\n // Opcode | Opcode + Arguments | Description | Stack View //\n //---------------------------------------------------------------------------------------------------------------//\n // 0x60 | 0x600B | PUSH1 11 | codeOffset //\n // 0x59 | 0x59 | MSIZE | 0 codeOffset //\n // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset //\n // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset //\n // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset //\n // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset //\n // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //\n // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //\n // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) //\n // 0xf3 | 0xf3 | RETURN | //\n //---------------------------------------------------------------------------------------------------------------//\n hex\"60_0B_59_81_38_03_80_92_59_39_F3\", // Returns all code in the contract except for the first 11 (0B in hex) bytes.\n runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.\n );\n\n /// @solidity memory-safe-assembly\n assembly {\n // Deploy a new contract with the generated creation code.\n // We start 32 bytes into the code to avoid copying the byte length.\n pointer := create(0, add(creationCode, 32), mload(creationCode))\n }\n\n require(pointer != address(0), \"DEPLOYMENT_FAILED\");\n }\n\n /*//////////////////////////////////////////////////////////////\n READ LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function read(address pointer) internal view returns (bytes memory) {\n return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);\n }\n\n function read(address pointer, uint256 start) internal view returns (bytes memory) {\n start += DATA_OFFSET;\n\n return readBytecode(pointer, start, pointer.code.length - start);\n }\n\n function read(\n address pointer,\n uint256 start,\n uint256 end\n ) internal view returns (bytes memory) {\n start += DATA_OFFSET;\n end += DATA_OFFSET;\n\n require(pointer.code.length >= end, \"OUT_OF_BOUNDS\");\n\n return readBytecode(pointer, start, end - start);\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL HELPER LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function readBytecode(\n address pointer,\n uint256 start,\n uint256 size\n ) private view returns (bytes memory data) {\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n data := mload(0x40)\n\n // Update the free memory pointer to prevent overriding our data.\n // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).\n // Adding 31 to size and running the result through the logic above ensures\n // the memory pointer remains word-aligned, following the Solidity convention.\n mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))\n\n // Store the size of the data in the first 32 byte chunk of free memory.\n mstore(data, size)\n\n // Copy the code into memory right after the 32 bytes we used to store the size.\n extcodecopy(pointer, add(data, 32), start, size)\n }\n }\n}\n"
|
||
}
|
||
},
|
||
"settings": {
|
||
"optimizer": {
|
||
"enabled": true,
|
||
"runs": 10000000
|
||
},
|
||
"evmVersion": "paris",
|
||
"outputSelection": {
|
||
"*": {
|
||
"*": [
|
||
"abi",
|
||
"evm.bytecode",
|
||
"evm.deployedBytecode",
|
||
"evm.methodIdentifiers",
|
||
"metadata",
|
||
"devdoc",
|
||
"userdoc",
|
||
"storageLayout",
|
||
"evm.gasEstimates"
|
||
],
|
||
"": [
|
||
"ast"
|
||
]
|
||
}
|
||
},
|
||
"metadata": {
|
||
"useLiteralContent": true
|
||
}
|
||
}
|
||
} |