fluid-contracts-public/deployments/mainnet/solcInputs/7ea6a03437971b2bd8979b983d3f0850.json
2024-07-11 13:05:09 +00:00

198 lines
285 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"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-users” 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-users” 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 Vaults 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 Vaults underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}\n"
},
"@openzeppelin/contracts/token/ERC20/extensions/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"
},
"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 view 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/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/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/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 | 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 | Chainlink Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant ChainlinkOracle__InvalidParams = 61001;\n\n /***********************************|\n | UniswapV3 Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant UniV3Oracle__InvalidParams = 62001;\n\n /// @notice thrown when constructor is called with invalid ordered seconds agos values\n uint256 internal constant UniV3Oracle__InvalidSecondsAgos = 62002;\n\n /// @notice thrown when constructor is called with invalid delta values > 100%\n uint256 internal constant UniV3Oracle__InvalidDeltas = 62003;\n\n /***********************************|\n | WstETh Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant WstETHOracle__InvalidParams = 63001;\n\n /***********************************|\n | Redstone Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant RedstoneOracle__InvalidParams = 64001;\n\n /***********************************|\n | Fallback Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant FallbackOracle__InvalidParams = 65001;\n\n /***********************************|\n | FallbackCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled)\n uint256 internal constant FallbackCLRSOracle__ExchangeRateZero = 66001;\n\n /***********************************|\n | WstETHCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled)\n uint256 internal constant WstETHCLRSOracle__ExchangeRateZero = 67001;\n\n /***********************************|\n | CLFallbackUniV3Oracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the uniV3 rate\n uint256 internal constant CLFallbackUniV3Oracle__ExchangeRateZero = 68001;\n\n /***********************************|\n | WstETHCLRS2UniV3CheckCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the uniV3 rate\n uint256 internal constant WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero = 69001;\n\n /***********************************|\n | WeETh Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant WeETHOracle__InvalidParams = 70001;\n}\n"
},
"contracts/oracle/fluidOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidOracle } from \"./interfaces/iFluidOracle.sol\";\n\n/// @title FluidOracle\n/// @notice Base contract that any Fluid Oracle must implement\nabstract contract FluidOracle is IFluidOracle {\n /// @inheritdoc IFluidOracle\n function getExchangeRate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_);\n}\n"
},
"contracts/oracle/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"
},
"contracts/oracle/libraries/FullMath.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\n/// @dev Modified from the original UniswapV3 library to support v0.8\n/// From: uint256 twos = -denominator & denominator;\n/// To: uint256 twos = (type(uint256).max - denominator + 1) & denominator;\n\n/// @title Contains 512-bit math functions\n/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision\n/// @dev Handles \"phantom overflow\" i.e., allows multiplication and division where an intermediate value overflows 256 bits\nlibrary FullMath {\n /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n /// @param a The multiplicand\n /// @param b The multiplier\n /// @param denominator The divisor\n /// @return result The 256-bit result\n /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv\n function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {\n // 512-bit multiply [prod1 prod0] = a * b\n // Compute the product mod 2**256 and mod 2**256 - 1\n // then use the Chinese Remainder Theorem to reconstruct\n // the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2**256 + prod0\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(a, b, not(0))\n prod0 := mul(a, b)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division\n if (prod1 == 0) {\n require(denominator > 0);\n assembly {\n result := div(prod0, denominator)\n }\n return result;\n }\n\n // Make sure the result is less than 2**256.\n // Also prevents denominator == 0\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0]\n // Compute remainder using mulmod\n uint256 remainder;\n assembly {\n remainder := mulmod(a, b, denominator)\n }\n // Subtract 256 bit number from 512 bit number\n assembly {\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator\n // Compute largest power of two divisor of denominator.\n // Always >= 1.\n /// @dev This line was modified for v0.8.x\n // uint256 twos = -denominator & denominator;\n uint256 twos = (type(uint256).max - denominator + 1) & denominator;\n // Divide denominator by power of two\n assembly {\n denominator := div(denominator, twos)\n }\n\n // Divide [prod1 prod0] by the factors of two\n assembly {\n prod0 := div(prod0, twos)\n }\n // Shift in bits from prod1 into prod0. For this we need\n // to flip `twos` such that it is 2**256 / twos.\n // If twos is zero, then it becomes one\n assembly {\n twos := add(div(sub(0, twos), twos), 1)\n }\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2**256\n // Now that denominator is an odd number, it has an inverse\n // modulo 2**256 such that denominator * inv = 1 mod 2**256.\n // Compute the inverse by starting with a seed that is correct\n // correct for four bits. That is, denominator * inv = 1 mod 2**4\n uint256 inv = (3 * denominator) ^ 2;\n // Now use Newton-Raphson iteration to improve the precision.\n // Thanks to Hensel's lifting lemma, this also works in modular\n // arithmetic, doubling the correct bits in each step.\n inv *= 2 - denominator * inv; // inverse mod 2**8\n inv *= 2 - denominator * inv; // inverse mod 2**16\n inv *= 2 - denominator * inv; // inverse mod 2**32\n inv *= 2 - denominator * inv; // inverse mod 2**64\n inv *= 2 - denominator * inv; // inverse mod 2**128\n inv *= 2 - denominator * inv; // inverse mod 2**256\n\n // Because the division is now exact we can divide by multiplying\n // with the modular inverse of denominator. This will give us the\n // correct result modulo 2**256. Since the precoditions guarantee\n // that the outcome is less than 2**256, this is the final result.\n // We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inv;\n return result;\n }\n\n /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n /// @param a The multiplicand\n /// @param b The multiplier\n /// @param denominator The divisor\n /// @return result The 256-bit result\n function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {\n result = mulDiv(a, b, denominator);\n if (mulmod(a, b, denominator) > 0) {\n require(result < type(uint256).max);\n result++;\n }\n }\n}\n"
},
"contracts/oracle/libraries/oracleUtils.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title Oracle utils library\n/// @notice implements common utility methods for Fluid Oracles\nlibrary OracleUtils {\n /// @dev The scaler for max delta point math (100%)\n uint256 internal constant HUNDRED_PERCENT_DELTA_SCALER = 10_000;\n /// @dev output precision of rates\n uint256 internal constant RATE_OUTPUT_DECIMALS = 27;\n\n /// @dev checks if `mainSourceRate_` is within a `maxDeltaPercent_` of `checkSourceRate_`. Returns true if so.\n function isRateOutsideDelta(\n uint256 mainSourceRate_,\n uint256 checkSourceRate_,\n uint256 maxDeltaPercent_\n ) internal pure returns (bool) {\n uint256 offset_ = (checkSourceRate_ * maxDeltaPercent_) / HUNDRED_PERCENT_DELTA_SCALER;\n return (mainSourceRate_ > (checkSourceRate_ + offset_) || mainSourceRate_ < (checkSourceRate_ - offset_));\n }\n}\n"
},
"contracts/oracle/libraries/TickMath.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.21;\n\n/// @dev Modified from the original UniswapV3 library to support v0.8\n/// From: require(absTick <= uint256(MAX_TICK), 'T');\n/// To: require(absTick <= uint256(int(MAX_TICK)), 'T');\n\n/// @title Math library for computing sqrt prices from ticks and vice versa\n/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports\n/// prices between 2**-128 and 2**128\nlibrary TickMath {\n /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128\n int24 internal constant MIN_TICK = -887272;\n /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128\n int24 internal constant MAX_TICK = -MIN_TICK;\n\n /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)\n uint160 internal constant MIN_SQRT_RATIO = 4295128739;\n /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)\n uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;\n\n /// @notice Calculates sqrt(1.0001^tick) * 2^96\n /// @dev Throws if |tick| > max tick\n /// @param tick The input tick for the above formula\n /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)\n /// at the given tick\n function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {\n uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n /// @dev This line was modified for v0.8.x\n // require(absTick <= uint256(MAX_TICK), 'T');\n require(absTick <= uint256(int(MAX_TICK)), \"T\");\n\n uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;\n if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;\n if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;\n if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;\n if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;\n if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;\n if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;\n if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;\n if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;\n if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;\n if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;\n if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;\n if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;\n if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;\n if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;\n if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;\n if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;\n if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;\n if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;\n if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;\n\n if (tick > 0) ratio = type(uint256).max / ratio;\n\n // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.\n // we then downcast because we know the result always fits within 160 bits due to our tick input constraint\n // we round up in the division so getTickAtSqrtRatio of the output price is always consistent\n sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));\n }\n\n /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio\n /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may\n /// ever return.\n /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96\n /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio\n function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {\n // second inequality must be < because the price can never reach the price at the max tick\n require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, \"R\");\n uint256 ratio = uint256(sqrtPriceX96) << 32;\n\n uint256 r = ratio;\n uint256 msb = 0;\n\n assembly {\n let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(5, gt(r, 0xFFFFFFFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(4, gt(r, 0xFFFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(3, gt(r, 0xFF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(2, gt(r, 0xF))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := shl(1, gt(r, 0x3))\n msb := or(msb, f)\n r := shr(f, r)\n }\n assembly {\n let f := gt(r, 0x1)\n msb := or(msb, f)\n }\n\n if (msb >= 128) r = ratio >> (msb - 127);\n else r = ratio << (127 - msb);\n\n int256 log_2 = (int256(msb) - 128) << 64;\n\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(63, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(62, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(61, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(60, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(59, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(58, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(57, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(56, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(55, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(54, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(53, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(52, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(51, f))\n r := shr(f, r)\n }\n assembly {\n r := shr(127, mul(r, r))\n let f := shr(128, r)\n log_2 := or(log_2, shl(50, f))\n }\n\n int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number\n\n int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);\n int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);\n\n tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;\n }\n}\n"
},
"contracts/oracle/oracles/cLFallbackUniV3Oracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { ChainlinkOracleImpl } from \"../implementations/chainlinkOracleImpl.sol\";\nimport { UniV3OracleImpl } from \"../implementations/uniV3OracleImpl.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title Chainlink with Fallback to UniV3 Oracle\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a Chainlink price feed or, if that feed fails, the price from a UniV3 TWAP delta checked Oracle.\ncontract CLFallbackUniV3Oracle is FluidOracle, ChainlinkOracleImpl, UniV3OracleImpl {\n /// @notice sets the Chainlink and UniV3 Oracle configs.\n /// @param chainlinkParams_ ChainlinkOracle constructor params struct.\n /// @param uniV3Params_ UniV3Oracle constructor params struct.\n constructor(\n ChainlinkConstructorParams memory chainlinkParams_,\n UniV3ConstructorParams memory uniV3Params_\n ) ChainlinkOracleImpl(chainlinkParams_) UniV3OracleImpl(uniV3Params_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view 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 mainSource_ which oracle to use as main source: 1 = Chainlink, 2 = Redstone (other one is fallback).\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n ) FallbackOracleImpl(mainSource_, chainlinkParams_, redstoneOracle_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view 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 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 {}\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(IERC4626 sUSDe_, uint8 debtTokenDecimals_) SUSDeOracleImpl(sUSDe_, debtTokenDecimals_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return _getSUSDeExchangeRate();\n }\n}\n"
},
"contracts/oracle/oracles/uniV3CheckCLRSOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { UniV3OracleImpl } from \"../implementations/uniV3OracleImpl.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title UniswapV3 checked against Chainlink / Redstone Oracle. Either one reported as exchange rate.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a UniV3 pool (compared against 3 TWAPs) and (optionally) comparing it against a Chainlink\n/// or Redstone price (one of Chainlink or Redstone being the main source and the other one the fallback source).\n/// Alternatively it can also use Chainlink / Redstone as main price and use UniV3 as check price.\n/// @dev The process for getting the aggregate oracle price is:\n/// 1. Fetch the UniV3 TWAPS, the latest interval is used as the current price\n/// 2. Verify this price is within an acceptable DELTA from the Uniswap TWAPS e.g.:\n/// a. 240 to 60s\n/// b. 60 to 15s\n/// c. 15 to 1s (last block)\n/// d. 1 to 0s (current)\n/// 3. (unless UniV3 only mode): Verify this price is within an acceptable DELTA from the Chainlink / Redstone Oracle\n/// 4. If it passes all checks, return the price. Otherwise use fallbacks, usually to Chainlink. In extreme edge-cases revert.\n/// @dev For UniV3 with check mode, if fetching the check price fails, the UniV3 rate is used directly.\ncontract UniV3CheckCLRSOracle is FluidOracle, UniV3OracleImpl, FallbackOracleImpl {\n /// @dev Rate check oracle delta percent in 1e2 percent. If current uniswap price is out of this delta,\n /// current price fetching reverts.\n uint256 internal immutable _RATE_CHECK_MAX_DELTA_PERCENT;\n\n /// @dev which oracle to use as final rate source:\n /// - 1 = UniV3 ONLY (no check),\n /// - 2 = UniV3 with Chainlink / Redstone check\n /// - 3 = Chainlink / Redstone with UniV3 used as check.\n uint8 internal immutable _RATE_SOURCE;\n\n struct UniV3CheckCLRSConstructorParams {\n /// @param uniV3Params UniV3Oracle constructor params struct.\n UniV3ConstructorParams uniV3Params;\n /// @param chainlinkParams ChainlinkOracle constructor params struct for UniV3CheckCLRSOracle.\n ChainlinkConstructorParams chainlinkParams;\n /// @param redstoneOracle Redstone Oracle data for UniV3CheckCLRSOracle. (address can be set to zero address if using Chainlink only)\n RedstoneOracleData redstoneOracle;\n /// @param rateSource which oracle to use as final rate source for UniV3CheckCLRSOracle:\n /// - 1 = UniV3 ONLY (no check),\n /// - 2 = UniV3 with Chainlink / Redstone check\n /// - 3 = Chainlink / Redstone with UniV3 used as check.\n uint8 rateSource;\n /// @param fallbackMainSource which oracle to use as CL/RS main source for UniV3CheckCLRSOracle: see FallbackOracleImpl constructor `mainSource_`\n uint8 fallbackMainSource;\n /// @param rateCheckMaxDeltaPercent Rate check oracle delta in 1e2 percent for UniV3CheckCLRSOracle\n uint256 rateCheckMaxDeltaPercent;\n }\n\n constructor(\n UniV3CheckCLRSConstructorParams memory params_\n )\n UniV3OracleImpl(params_.uniV3Params)\n FallbackOracleImpl(params_.fallbackMainSource, params_.chainlinkParams, params_.redstoneOracle)\n {\n if (\n params_.rateSource < 1 ||\n params_.rateSource > 3 ||\n params_.rateCheckMaxDeltaPercent > OracleUtils.HUNDRED_PERCENT_DELTA_SCALER ||\n // Chainlink only Oracle with UniV3 check. Delta would be ignored so revert this type of Oracle setup.\n (params_.fallbackMainSource == 1 && params_.rateSource == 3)\n ) {\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidParams);\n }\n\n _RATE_CHECK_MAX_DELTA_PERCENT = params_.rateCheckMaxDeltaPercent;\n _RATE_SOURCE = params_.rateSource;\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view virtual override returns (uint256 exchangeRate_) {\n return _getExchangeRate();\n }\n\n /// @notice returns all oracle related data as utility for easy off-chain / block explorer use in a single view method\n function uniV3CheckOracleData()\n public\n view\n returns (uint256 rateCheckMaxDelta_, uint256 rateSource_, uint256 fallbackMainSource_)\n {\n return (_RATE_CHECK_MAX_DELTA_PERCENT, _RATE_SOURCE, _FALLBACK_ORACLE_MAIN_SOURCE);\n }\n\n function _getExchangeRate() internal view returns (uint256 exchangeRate_) {\n if (_RATE_SOURCE == 1) {\n // uniswap is the only main source without check:\n // 1. get uniV3 rate.\n // 2. If that fails (outside delta range) -> revert (no other Oracle configured).\n exchangeRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n // fetching UniV3 failed or invalid delta -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n return exchangeRate_;\n }\n\n uint256 checkRate_;\n bool fallback_;\n if (_RATE_SOURCE == 2) {\n // uniswap is main source, with Chainlink / Redstone as check\n // 1. get uniV3 rate\n\n // case uniV3 rate fails (outside delta range):\n // 2. get Chainlink rate. -> if successful, use Chainlink as result\n // 3. if Chainlink fails too, get Redstone -> if successful, use Redstone as result\n // 4. if Redstone fails too, revert\n\n // case if uniV3 rate is ok\n // 2. get Chainlink or Redstone rate for check (one is configured as main check source, other one is fallback source)\n // -> if both fail to fetch, use uniV3 rate directly.\n // 3. check the delta for uniV3 rate against the check soure rate. -> if ok, return uniV3 rate\n // 4. if delta check fails, check delta against the fallback check source. -> if ok, return uniV3 rate\n // 5. if delta check fails for both sources, return Chainlink price\n\n exchangeRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n // uniV3 failed or invalid delta -> use (Chainlink with Redstone as fallback)\n exchangeRate_ = _getChainlinkOrRedstoneAsFallback();\n if (exchangeRate_ == 0) {\n // Chainlink / Redstone failed too -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n return exchangeRate_;\n }\n\n (checkRate_, fallback_) = _getRateWithFallback();\n if (checkRate_ == 0) {\n // check price source failed to fetch -> directly use uniV3 TWAP checked price\n // Note uniV3 price fetching was successful, would have been caught otherwise above.\n return exchangeRate_;\n }\n } else {\n // Chainlink / Redstone is main source, with uniV3 as check.\n // 1. get Chainlink / Redstone rate (one is configured as main source, other one is fallback source)\n\n // case when both Chainlink & Redstone fail:\n // 2. get uniV3 rate. if successful, use uniV3 rate. otherwise, revert (all oracles failed).\n\n // case when Chainlink / Redstone fetch is successful:\n // 2. get uniV3 rate for check.\n // 3. if uniV3 rate fails to fetch (outside delta), use Chainlink / Redstone directly (skip check).\n // 4. if uniV3 rate is ok, check the delta for Chainlink / Redstone rate against uniV3 rate.\n // -> if ok, return Chainlink / Redstone (main) rate\n // 5. if delta check fails, check delta against the fallback main source.\n // -> if ok, return fallback main rate\n // 6. if delta check fails for both sources, return Chainlink price.\n\n (exchangeRate_, fallback_) = _getRateWithFallback();\n checkRate_ = _getUniV3ExchangeRate();\n\n if (exchangeRate_ == 0) {\n if (checkRate_ == 0) {\n // all oracles failed, revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // Both Chainlink & Redstone failed -> directly use uniV3 TWAP checked price\n // Note uniV3 price fetching was successful, would have been caught otherwise above.\n return checkRate_;\n }\n\n if (checkRate_ == 0) {\n // uniV3 failed -> skip check against Uniswap price.\n\n return exchangeRate_;\n }\n }\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n if (fallback_) {\n // fallback already used, no other rate available to check.\n\n // if price is chainlink price -> return it.\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 3) {\n // redstone with Chainlink as fallback\n return _RATE_SOURCE == 2 ? checkRate_ : exchangeRate_; // if rate source is 2, Chainlink rate is in checkRate_\n }\n\n // if price is redstone price -> revert\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidPrice);\n }\n\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 1) {\n // 1 = only chainlink and UniV3 is configured and delta check failed. no fallback available.\n if (_RATE_SOURCE == 2) {\n // case where uniV3 is main source with only Chainlink as check rate Oracle configured.\n // delta check failed -> return Chainlink price (instead of uniV3 price).\n return checkRate_;\n }\n\n // here: if (_FALLBACK_ORACLE_MAIN_SOURCE == 1 && _RATE_SOURCE == 3)\n // rate source is 3: Chainlink as main, uniV3 as delta. delta check failed.\n // this Oracle type would basically be a more expensive Chainlink-only Oracle because the delta check against UniV3 is ignored.\n // this setup is reverted in constructor, but in any case returning Chainlink price here even though this code should never be reached.\n return exchangeRate_; // exchangeRate_ here is chainlink price\n }\n\n // fallback not done yet -> check against fallback price.\n // So if originally Chainlink was fetched and delta failed, check against Redstone.\n // if originally Redstone was fetched and delta failed, check against Chainlink.\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 2) {\n // 2 = Chainlink with Redstone Fallback. delta check against Chainlink failed. try against Redstone.\n uint256 redstoneRate_ = _getRedstoneExchangeRate();\n uint256 chainlinkRate_;\n if (_RATE_SOURCE == 2) {\n // uniV3 main source. -> update checkRate_ with Redstone price\n chainlinkRate_ = checkRate_;\n checkRate_ = redstoneRate_;\n } else {\n // uniV3 is check source. -> update exchangeRate_ with Redstone price\n chainlinkRate_ = exchangeRate_;\n exchangeRate_ = redstoneRate_;\n }\n\n if (redstoneRate_ == 0) {\n // fetching Redstone failed. So delta UniV3 <> Chainlink failed, fetching Redstone as backup failed.\n // -> return chainlink price (for both cases when Chainlink is main and when UniV3 is the main source).\n return chainlinkRate_;\n }\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n // delta check against Redstone failed too. return Chainlink price\n return chainlinkRate_;\n }\n\n // delta check against Redstone passed. if uniV3 main source -> return uniV3, else return Redstone.\n // exchangeRate_ is already set correctly for this.\n } else {\n // 3 = Redstone with Chainlink Fallback. delta check against Redstone failed. try against Chainlink.\n uint256 chainlinkRate_ = _getChainlinkExchangeRate();\n if (chainlinkRate_ == 0) {\n // fetching Chainlink failed. So delta UniV3 <> Redstone failed, fetching Chainlink as backup check failed.\n // -> revert.\n revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidPrice);\n }\n\n if (_RATE_SOURCE == 3) {\n // uniV3 is check source. -> update exchangeRate_ with Chainlink price.\n // Optimization: in this case we can directly return chainlink price, because if delta check between\n // Chainlink (new main source) and uniV3 (check source) fails, we anyway return Chainlink price still.\n return chainlinkRate_;\n }\n\n // uniV3 main source. -> update checkRate_ with Chainlink price and compare delta again\n checkRate_ = chainlinkRate_;\n\n if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {\n // delta check against Chainlink failed too. case here can only be where uniV3 would have been\n // main source and Chainlink check source. -> return Chainlink as price instead of uniV3\n return checkRate_;\n }\n\n // delta check against Chainlink passed. if uniV3 main source -> return uniV3, else return Chainlink.\n // exchangeRate_ is already set correctly for this.\n }\n }\n }\n}\n"
},
"contracts/oracle/oracles/weETHOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\n\n/// @title WeETHOracle\n/// @notice Gets the exchange rate between weETH and eETH directly from the weETH contract.\ncontract WeETHOracle is FluidOracle, WeETHOracleImpl {\n /// @notice constructor sets the weETH `weETH_` token address.\n constructor(IWeETH weETH_) WeETHOracleImpl(weETH_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getWeETHExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
},
"contracts/oracle/oracles/weETHUniV3CheckCLRSOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { UniV3CheckCLRSOracle } from \"./uniV3CheckCLRSOracle.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title weETHOracle combined with a uniV3CheckCLRSOracle.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// 1. weETH Oracle price for weETH -> eETH = ETH (pegged)\n/// 2. result from 1. combined with a uniV3CheckCLRSOracle to get someToken (e.g. ETH) -> someToken2.\n/// e.g. when going from weETH to USDC:\n/// 1. weETH -> eETH = ETH via weETH Oracle\n/// 2. ETH -> USDC via UniV3 ETH <> USDC pool checked against ETH -> USDC Chainlink feed.\ncontract WeETHUniV3CheckCLRSOracle is FluidOracle, WeETHOracleImpl, UniV3CheckCLRSOracle {\n /// @notice constructs a WeETHUniV3CheckCLRSOracle with all inherited contracts\n /// @param weETH_ address of the weETH contract\n /// @param uniV3CheckCLRSParams_ UniV3CheckCLRSOracle constructor params\n constructor(\n IWeETH weETH_,\n UniV3CheckCLRSConstructorParams memory uniV3CheckCLRSParams_\n ) WeETHOracleImpl(weETH_) UniV3CheckCLRSOracle(uniV3CheckCLRSParams_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0.\n // (super.getExchangeRate() returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n // Combine this rate with the weETH -> eETH = ETH rate.\n exchangeRate_ =\n (super.getExchangeRateOperate() * _getWeETHExchangeRate()) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0.\n // (super.getExchangeRate() returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n // Combine this rate with the weETH -> eETH = ETH rate.\n exchangeRate_ =\n (super.getExchangeRateLiquidate() * _getWeETHExchangeRate()) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view override(FluidOracle, UniV3CheckCLRSOracle) returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
},
"contracts/oracle/oracles/weETHwstETHOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { WeETHOracleImpl } from \"../implementations/weETHOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { IWeETH } from \"../interfaces/external/IWeETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\n\n/// @title Oracle for weETH (Etherfi's wrapped eETH) to wstETH. wstETH is the debt token here (get amount of wstETH for 1 weETH)\ncontract WeETHWstETHOracle is FluidOracle, WstETHOracleImpl, WeETHOracleImpl {\n /// @param wstETH address of the wstETH contract\n /// @param weETH address of the weETH contract\n constructor(IWstETH wstETH, IWeETH weETH) WstETHOracleImpl(wstETH) WeETHOracleImpl(weETH) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n // weEth -> wstETH\n exchangeRate_ =\n (_WEETH.getEETHByWeETH(1e18) * (10 ** OracleUtils.RATE_OUTPUT_DECIMALS)) /\n _WSTETH.stEthPerToken();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
},
"contracts/oracle/oracles/wstETHCLRS2UniV3CheckCLRSOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { UniV3CheckCLRSOracle } from \"./uniV3CheckCLRSOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { FallbackOracleImpl2 } from \"../implementations/fallbackOracleImpl2.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n// @dev uses FallbackOracleImpl2 to avoid conflicts with already used ChainlinkOracleImpl, RedstoneOracleImpl and\n// FallbackOracleImpl in UniV3CheckCLRSOracle.\n\n/// @title wstETHCLRSOracle combined with a uniV3CheckCLRSOracle.\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// 1. wstETH Oracle price in combination with rate from Chainlink price feeds (or Redstone as fallback).\n/// combining those two into one rate resulting in wstETH <> someToken\n/// 2. result from 1. combined with a uniV3CheckCLRSOracle to get from someToken <> someToken2\n/// e.g. when going from wstETH to USDC:\n/// 1. wstETH -> stETH wstETH Oracle, stETH -> ETH Chainlink feed.\n/// 2. ETH -> USDC via UniV3 ETH <> USDC pool checked against ETH -> USDC Chainlink feed.\ncontract WstETHCLRS2UniV3CheckCLRSOracle is FluidOracle, WstETHOracleImpl, FallbackOracleImpl2, UniV3CheckCLRSOracle {\n struct WstETHCLRS2ConstructorParams {\n /// @param wstETH address of the wstETH contract\n IWstETH wstETH;\n /// @param fallbackMainSource which oracle to use as main source for wstETH <> CLRS: 1 = Chainlink, 2 = Redstone (other one is fallback).\n uint8 fallbackMainSource;\n /// @param chainlinkParams chainlink Oracle constructor params struct for wstETH <> CLRS.\n ChainlinkConstructorParams chainlinkParams;\n /// @param redstoneOracle Redstone Oracle data for wstETH <> CLRS. (address can be set to zero address if using Chainlink only)\n RedstoneOracleData redstoneOracle;\n }\n\n /// @notice constructs a WstETHCLRS2UniV3CheckCLRSOracle with all inherited contracts\n /// @param wstETHCLRS2Params_ WstETHCLRS2ConstructorParams for wstETH <> CLRS Token2 conversion\n /// @param uniV3CheckCLRSParams_ UniV3CheckCLRSOracle constructor params\n constructor(\n WstETHCLRS2ConstructorParams memory wstETHCLRS2Params_,\n UniV3CheckCLRSConstructorParams memory uniV3CheckCLRSParams_\n )\n WstETHOracleImpl(wstETHCLRS2Params_.wstETH)\n FallbackOracleImpl2(\n wstETHCLRS2Params_.fallbackMainSource,\n wstETHCLRS2Params_.chainlinkParams,\n wstETHCLRS2Params_.redstoneOracle\n )\n UniV3CheckCLRSOracle(uniV3CheckCLRSParams_)\n {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate for stETH <> CLRS feed. uses FallbackOracleImpl2\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // 2. combine CLRS feed price with wstETH price to have wstETH <> stETH <> SomeToken fully converted\n exchangeRate_ = (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n // 3. get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0. Combine this rate with existing.\n // (super.getExchangeRate returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n exchangeRate_ = (super.getExchangeRateOperate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate()\n public\n view\n override(FluidOracle, UniV3CheckCLRSOracle)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate for stETH <> CLRS feed. uses FallbackOracleImpl2\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero);\n }\n\n // 2. combine CLRS feed price with wstETH price to have wstETH <> stETH <> SomeToken fully converted\n exchangeRate_ = (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n // 3. get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns\n // a price if some rate is valid, with multiple fallbacks. Can not return 0. Combine this rate with existing.\n // (super.getExchangeRate returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)\n exchangeRate_ = (super.getExchangeRateLiquidate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view override(FluidOracle, UniV3CheckCLRSOracle) returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE2_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE2_MAIN_SOURCE;\n }\n}\n"
},
"contracts/oracle/oracles/wstETHCLRSOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title WstETH Chainlink / Redstone Oracle (with fallback)\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// wstETH Oracle price in combination with rate from Chainlink price feeds (or Redstone as fallback),\n/// hopping the 2 rates into 1 rate.\n/// e.g. when going from wstETH to USDT:\n/// wstETH -> stETH wstETH Oracle, stETH -> ETH Chainlink feed, ETH -> USDT Chainlink feed.\ncontract WstETHCLRSOracle is FluidOracle, WstETHOracleImpl, FallbackOracleImpl {\n /// @notice sets the wstETH address, main source, Chainlink Oracle and Redstone Oracle data.\n /// @param wstETH_ address of the wstETH contract\n /// @param mainSource_ which oracle to use as main source: 1 = Chainlink, 2 = Redstone (other one is fallback).\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n IWstETH wstETH_,\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n ) WstETHOracleImpl(wstETH_) FallbackOracleImpl(mainSource_, chainlinkParams_, redstoneOracle_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n (exchangeRate_, ) = _getRateWithFallback();\n\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.WstETHCLRSOracle__ExchangeRateZero);\n }\n\n return (_getWstETHExchangeRate() * exchangeRate_) / (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE_MAIN_SOURCE;\n }\n}\n"
},
"contracts/oracle/oracles/wstETHOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { WstETHOracleImpl } from \"../implementations/wstETHOracleImpl.sol\";\nimport { IWstETH } from \"../interfaces/external/IWstETH.sol\";\n\n/// @title WstETHOracle\n/// @notice Gets the exchange rate between wstETH and stETH directly from the wstETH contract.\ncontract WstETHOracle is FluidOracle, WstETHOracleImpl {\n /// @notice constructor sets the wstETH `wstETH_` token address.\n constructor(IWstETH wstETH_) WstETHOracleImpl(wstETH_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view override returns (uint256 exchangeRate_) {\n return _getWstETHExchangeRate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() external view override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n}\n"
},
"contracts/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 chainlinkParams_ ChainlinkOracle constructor params struct.\n /// @param uniV3Params_ UniV3Oracle constructor params struct.\n /// @param sequencerUptimeFeed_ L2 sequencer uptime Chainlink feed\n constructor(\n ChainlinkConstructorParams memory chainlinkParams_,\n UniV3ConstructorParams memory uniV3Params_,\n address sequencerUptimeFeed_\n ) CLFallbackUniV3Oracle(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/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 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 uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_,\n address sequencerUptimeFeed_\n ) FallbackCLRSOracle(mainSource_, chainlinkParams_, redstoneOracle_) FluidOracleL2(sequencerUptimeFeed_) {}\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 UniV3CheckCLRSConstructorParams memory params_,\n address sequencerUptimeFeed_\n ) UniV3CheckCLRSOracle(params_) FluidOracleL2(sequencerUptimeFeed_) {}\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateOperate()\n public\n view\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 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 override(UniV3CheckCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n return getExchangeRateOperate();\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"
}
},
"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
}
}
}