mirror of
https://github.com/Instadapp/fluid-contracts-public.git
synced 2024-07-29 21:57:37 +00:00
d7a58e88ff
ARB: deploy protocols
117 lines
244 KiB
JSON
117 lines
244 KiB
JSON
{
|
|
"language": "Solidity",
|
|
"sources": {
|
|
"@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721Enumerable is IERC721 {\n /**\n * @dev Returns the total amount of tokens stored by the contract.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns a token ID owned by `owner` at a given `index` of its token list.\n * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.\n */\n function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);\n\n /**\n * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.\n * Use along with {totalSupply} to enumerate all tokens.\n */\n function tokenByIndex(uint256 index) external view returns (uint256);\n}\n"
|
|
},
|
|
"@openzeppelin/contracts/token/ERC721/IERC721.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721\n * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n * understand this adds an external call which potentially creates a reentrancy vulnerability.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"
|
|
},
|
|
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
|
|
},
|
|
"contracts/infiniteProxy/interfaces/iProxy.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IProxy {\n function setAdmin(address newAdmin_) external;\n\n function setDummyImplementation(address newDummyImplementation_) external;\n\n function addImplementation(address implementation_, bytes4[] calldata sigs_) external;\n\n function removeImplementation(address implementation_) external;\n\n function getAdmin() external view returns (address);\n\n function getDummyImplementation() external view returns (address);\n\n function getImplementationSigs(address impl_) external view returns (bytes4[] memory);\n\n function getSigsImplementation(bytes4 sig_) external view returns (address);\n\n function readFromStorage(bytes32 slot_) external view returns (uint256 result_);\n}\n"
|
|
},
|
|
"contracts/libraries/bigMathMinified.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.\n/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision\n/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can\n/// result in significant gas cost reduction due to storage space reduction.\n/// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision.\n/// @dev roundUp is more like a increase 1, which happens everytime for the same number.\n/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number.\nlibrary BigMathMinified {\n /// @dev constants to use for `roundUp` input param to increase readability\n bool internal constant ROUND_DOWN = false;\n bool internal constant ROUND_UP = true;\n\n /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).\n /// e.g.:\n /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]\n /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)\n /// => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000\n /// ^-------------------- 51(exponent) -------------- ^\n /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563)\n /// exponent = 0011,0011 (51)\n /// bigNumber = 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 (572493200179)\n ///\n /// @param normal number which needs to be converted into Big Number\n /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))\n /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))\n /// @param roundUp signals if result should be rounded down or up\n /// @return bigNumber converted bigNumber (coefficient << exponent)\n function toBigNumber(\n uint256 normal,\n uint256 coefficientSize,\n uint256 exponentSize,\n bool roundUp\n ) internal pure returns (uint256 bigNumber) {\n assembly {\n let lastBit_\n let number_ := normal\n if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit_ := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit_ := add(lastBit_, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit_ := add(lastBit_, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit_ := add(lastBit_, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit_ := add(lastBit_, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit_ := add(lastBit_, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit_ := add(lastBit_, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit_ := add(lastBit_, 1)\n }\n if gt(number_, 0) {\n lastBit_ := add(lastBit_, 1)\n }\n if lt(lastBit_, coefficientSize) {\n // for throw exception\n lastBit_ := coefficientSize\n }\n let exponent := sub(lastBit_, coefficientSize)\n let coefficient := shr(exponent, normal)\n if and(roundUp, gt(exponent, 0)) {\n // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number\n coefficient := add(coefficient, 1)\n if eq(shl(coefficientSize, 1), coefficient) {\n // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits\n // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.\n coefficient := shl(sub(coefficientSize, 1), 1)\n exponent := add(exponent, 1)\n }\n }\n if iszero(lt(exponent, shl(exponentSize, 1))) {\n // if exponent is >= exponentSize, the normal number is too big to fit within\n // BigNumber with too small sizes for coefficient and exponent\n revert(0, 0)\n }\n bigNumber := shl(exponentSize, coefficient)\n bigNumber := add(bigNumber, exponent)\n }\n }\n\n /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`\n function fromBigNumber(\n uint256 bigNumber,\n uint256 exponentSize,\n uint256 exponentMask\n ) internal pure returns (uint256 normal) {\n assembly {\n let coefficient := shr(exponentSize, bigNumber)\n let exponent := and(bigNumber, exponentMask)\n normal := shl(exponent, coefficient)\n }\n }\n\n /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).\n /// e.g.\n /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111\n /// lastBit = ^--------------------------------- 83 ----------------------------------------^\n function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {\n assembly {\n let number_ := normal\n if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {\n number_ := shr(0x80, number_)\n lastBit := 0x80\n }\n if gt(number_, 0xFFFFFFFFFFFFFFFF) {\n number_ := shr(0x40, number_)\n lastBit := add(lastBit, 0x40)\n }\n if gt(number_, 0xFFFFFFFF) {\n number_ := shr(0x20, number_)\n lastBit := add(lastBit, 0x20)\n }\n if gt(number_, 0xFFFF) {\n number_ := shr(0x10, number_)\n lastBit := add(lastBit, 0x10)\n }\n if gt(number_, 0xFF) {\n number_ := shr(0x8, number_)\n lastBit := add(lastBit, 0x8)\n }\n if gt(number_, 0xF) {\n number_ := shr(0x4, number_)\n lastBit := add(lastBit, 0x4)\n }\n if gt(number_, 0x3) {\n number_ := shr(0x2, number_)\n lastBit := add(lastBit, 0x2)\n }\n if gt(number_, 0x1) {\n lastBit := add(lastBit, 1)\n }\n if gt(number_, 0) {\n lastBit := add(lastBit, 1)\n }\n }\n }\n}\n"
|
|
},
|
|
"contracts/libraries/bigMathVault.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { BigMathMinified } from \"./bigMathMinified.sol\";\n\n/// @title Extended version of BigMathMinified. Implements functions for normal operators (*, /, etc) modified to interact with big numbers.\n/// @notice this is an optimized version mainly created by taking Fluid vault's codebase into consideration so it's use is limited for other cases.\nlibrary BigMathVault {\n uint private constant COEFFICIENT_SIZE_DEBT_FACTOR = 35;\n uint private constant EXPONENT_SIZE_DEBT_FACTOR = 15;\n uint private constant COEFFICIENT_MAX_DEBT_FACTOR = (1 << COEFFICIENT_SIZE_DEBT_FACTOR) - 1;\n uint private constant EXPONENT_MAX_DEBT_FACTOR = (1 << EXPONENT_SIZE_DEBT_FACTOR) - 1;\n uint private constant DECIMALS_DEBT_FACTOR = 16384;\n uint internal constant MAX_MASK_DEBT_FACTOR = (1 << (COEFFICIENT_SIZE_DEBT_FACTOR + EXPONENT_SIZE_DEBT_FACTOR)) - 1;\n\n // Having precision as 2**64 on vault\n uint internal constant PRECISION = 64;\n uint internal constant TWO_POWER_64 = 1 << PRECISION;\n // Max bit for 35 bits * 35 bits number will be 70\n // why do we use 69 then here instead of 70\n uint internal constant TWO_POWER_69_MINUS_1 = (1 << 69) - 1;\n\n uint private constant COEFFICIENT_PLUS_PRECISION = COEFFICIENT_SIZE_DEBT_FACTOR + PRECISION; // 99\n uint private constant COEFFICIENT_PLUS_PRECISION_MINUS_1 = COEFFICIENT_PLUS_PRECISION - 1; // 98\n uint private constant TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1 = (1 << COEFFICIENT_PLUS_PRECISION_MINUS_1) - 1; // (1 << 98) - 1;\n uint private constant TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1_MINUS_1 =\n (1 << (COEFFICIENT_PLUS_PRECISION_MINUS_1 - 1)) - 1; // (1 << 97) - 1;\n\n /// @dev multiplies a `normal` number with a `bigNumber1` and then divides by `bigNumber2`.\n /// @dev For vault's use case MUST always:\n /// - bigNumbers have exponent size 15 bits\n /// - bigNumbers have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)\n /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.\n /// - bigNumber1 (debt factor) always have exponent >= 1 & <= 16384\n /// - bigNumber2 (connection factor) always have exponent >= 1 & <= 32767 (15 bits)\n /// - bigNumber2 always >= bigNumber1 (connection factor can never be < base branch debt factor)\n /// - as a result of previous points, numbers must never be 0\n /// - normal is positionRawDebt and is always within 10000 and type(int128).max\n /// @return normal * bigNumber1 / bigNumber2\n function mulDivNormal(uint256 normal, uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {\n unchecked {\n // exponent2_ - exponent1_\n uint netExponent_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) - (bigNumber1 & EXPONENT_MAX_DEBT_FACTOR);\n if (netExponent_ < 129) {\n // (normal * coefficient1_) / (coefficient2_ << netExponent_);\n return ((normal * (bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR)) /\n ((bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR) << netExponent_));\n }\n // else:\n // biggest possible nominator: type(int128).max * 35bits max = 5846006549323611672814739330865132078589370433536\n // smallest possible denominator: 17179869184 << 129 (= 1 << 163) = 11692013098647223345629478661730264157247460343808\n // -> can only ever be 0\n return 0;\n }\n }\n\n /// @dev multiplies a `bigNumber` with normal `number1` and then divides by `TWO_POWER_64`.\n /// @dev For vault's use case (calculating new branch debt factor after liquidation):\n /// - number1 is debtFactor, intialized as TWO_POWER_64 and reduced from there, hence it's always <= TWO_POWER_64 and always > 0.\n /// - bigNumber is branch debt factor, which starts as ((X35 << 15) | (1 << 14)) and reduces from there.\n /// - bigNumber must have have exponent size 15 bits and be >= 1 & <= 16384\n /// - bigNumber must have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)\n /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.\n /// @param bigNumber Coefficient | Exponent.\n /// @param number1 normal number.\n /// @return result bigNumber * number1 / TWO_POWER_64.\n function mulDivBigNumber(uint256 bigNumber, uint256 number1) internal pure returns (uint256 result) {\n // using unchecked as we are only at 1 place in Vault and it won't overflow there.\n unchecked {\n uint256 _resultNumerator = (bigNumber >> EXPONENT_SIZE_DEBT_FACTOR) * number1; // bigNumber coefficient * normal number\n // 99% chances are that most sig bit should be 64 + 35 - 1 or 64 + 35 - 2\n // diff = mostSigBit. Can only ever be >= 35 and <= 98\n uint256 diff = (_resultNumerator > TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1)\n ? COEFFICIENT_PLUS_PRECISION\n : (_resultNumerator > TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1_MINUS_1)\n ? COEFFICIENT_PLUS_PRECISION_MINUS_1\n : BigMathMinified.mostSignificantBit(_resultNumerator);\n\n // diff = difference in bits to make the _resultNumerator 35 bits again\n diff = diff - COEFFICIENT_SIZE_DEBT_FACTOR;\n _resultNumerator = _resultNumerator >> diff;\n // starting exponent is 16384, so exponent should never get 0 here\n result = (bigNumber & EXPONENT_MAX_DEBT_FACTOR) + diff;\n if (result > PRECISION) {\n result = (_resultNumerator << EXPONENT_SIZE_DEBT_FACTOR) + result - PRECISION; // divides by TWO_POWER_64 by reducing exponent by 64\n } else {\n // if number1 is small, e.g. 1e4 and bigNumber is also small e.g. coefficient = 17179869184 & exponent is at 50\n // then: resultNumerator = 171798691840000, diff most significant bit = 48, ending up with diff = 13\n // for exponent in result we end up doing: 50 + 13 - 64 -> underflowing exponent.\n // this should never happen anyway, but if it does better to revert than to continue with unknown effects.\n revert(); // debt factor should never become a BigNumber with exponent <= 0\n }\n }\n }\n\n /// @dev multiplies a `bigNumber1` with another `bigNumber2`.\n /// @dev For vault's use case (calculating connection factor of merged branches userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor):\n /// - bigNumbers must have have exponent size 15 bits and be >= 1 & <= 32767\n /// - bigNumber must have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)\n /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.\n /// @dev sum of exponents from `bigNumber1` `bigNumber2` should be > 16384.\n /// e.g. res = bigNumber1 * bigNumber2 = [(coe1, exp1) * (coe2, exp2)] >> decimal\n /// = (coe1*coe2>>overflow, exp1+exp2+overflow-decimal)\n /// @param bigNumber1 BigNumber format with coefficient and exponent.\n /// @param bigNumber2 BigNumber format with coefficient and exponent.\n /// @return BigNumber format with coefficient and exponent\n function mulBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {\n unchecked {\n // coefficient1_ * coefficient2_\n uint resCoefficient_ = (bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) *\n (bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR);\n // res coefficient at min can be 17179869184 * 17179869184 = 295147905179352825856 (= 1 << 68; 69th bit as 1)\n // res coefficient at max can be 34359738367 * 34359738367 = 1180591620648691826689 (X35 * X35 fits in 70 bits)\n uint overflowLen_ = resCoefficient_ > TWO_POWER_69_MINUS_1\n ? COEFFICIENT_SIZE_DEBT_FACTOR\n : COEFFICIENT_SIZE_DEBT_FACTOR - 1;\n // overflowLen_ is either 34 or 35\n resCoefficient_ = resCoefficient_ >> overflowLen_;\n\n // bigNumber2 is connection factor\n // exponent1_ + exponent2_ + overflowLen_ - decimals\n uint resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) +\n (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) +\n overflowLen_);\n if (resExponent_ < DECIMALS_DEBT_FACTOR) {\n // for this ever to happen, the debt factors used to calculate connection factors would have to be at extremely\n // unrealistic values. Like e.g.\n // branch3 (debt factor X35 << 15 | 16383) got merged into branch2 (debt factor X35 << 15 | 8190)\n // -> connection factor (divBigNumber): ((coe1<<precision_)/coe2>>overflowLen, exp1+decimal+overflowLen-exp2-precision_) so:\n // coefficient: (X35<<64)/X35 >> 30 = 17179869184\n // exponent: 8190+16384+30-16383-64 = 8157.\n // result: 17179869184 << 15 | 8157\n // and then branch2 into branch1 (debt factor X35 << 15 | 22). -> connection factor:\n // coefficient: (X35<<64)/X35 >> 30 = 17179869184\n // exponent: 22+16384+30-8190-64 = 8182.\n // result: 17179869184 << 15 | 8182\n // connection factors sum up (mulBigNumber): (coe1*coe2>>overflow, exp1+exp2+overflow-decimal)\n // exponent: 8182+8157+35-16384=16374-16384=-10. underflow.\n // this should never happen anyway, but if it does better to revert than to continue with unknown effects.\n revert();\n }\n resExponent_ = resExponent_ - DECIMALS_DEBT_FACTOR;\n\n if (resExponent_ > EXPONENT_MAX_DEBT_FACTOR) {\n // if resExponent_ is not within limits that means user's got ~100% (something like 99.999999999999...)\n // this situation will probably never happen and this basically means user's position is ~100% liquidated\n return MAX_MASK_DEBT_FACTOR;\n }\n\n return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_);\n }\n }\n\n /// @dev divides a `bigNumber1` by `bigNumber2`.\n /// @dev For vault's use case (calculating connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor) bigNumbers MUST always:\n /// - have exponent size 15 bits and be >= 1 & <= 16384\n /// - have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision)\n /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367.\n /// - as a result of previous points, numbers must never be 0\n /// e.g. res = bigNumber1 / bigNumber2 = [(coe1, exp1) / (coe2, exp2)] << decimal\n /// = ((coe1<<precision_)/coe2, exp1+decimal-exp2-precision_)\n /// @param bigNumber1 BigNumber format with coefficient and exponent\n /// @param bigNumber2 BigNumber format with coefficient and exponent\n /// @return BigNumber format with coefficient and exponent\n /// Returned connection factor can only ever be >= baseBranchDebtFactor (c = x*100/y with both x,y > 0 & x,y <= 100: c can only ever be >= x)\n function divBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) {\n unchecked {\n // (coefficient1_ << PRECISION) / coefficient2_\n uint256 resCoefficient_ = ((bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) << PRECISION) /\n (bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR);\n // nominator at min 17179869184 << 64 = 316912650057057350374175801344. at max 34359738367 << 64 = 633825300095667956674642051072.\n // so min value resCoefficient_ 9223372037123211264 (64 bits) vs max 36893488146345361408 (fits in 65 bits)\n\n // mostSigBit will be PRECISION + 1 or PRECISION\n uint256 overflowLen_ = ((resCoefficient_ >> PRECISION) == 1) ? (PRECISION + 1) : PRECISION;\n // Overflow will be PRECISION - COEFFICIENT_SIZE_DEBT_FACTOR or (PRECISION + 1) - COEFFICIENT_SIZE_DEBT_FACTOR\n // Meaning 64 - 35 = 29 or 65 - 35 = 30\n overflowLen_ = overflowLen_ - COEFFICIENT_SIZE_DEBT_FACTOR;\n resCoefficient_ = resCoefficient_ >> overflowLen_;\n\n // exponent1_ will always be less than or equal to 16384\n // exponent2_ will always be less than or equal to 16384\n // Even if exponent2_ is 0 (not possible) & resExponent_ = DECIMALS_DEBT_FACTOR then also resExponent_ will be less than max limit, so no overflow\n // result exponent = (exponent1_ + DECIMALS_DEBT_FACTOR + overflowLen_) - (exponent2_ + PRECISION);\n uint256 resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) + // exponent1_\n DECIMALS_DEBT_FACTOR + // DECIMALS_DEBT_FACTOR is 100% as it is percentage value\n overflowLen_); // addition part resExponent_ here min 16414, max 32798\n // reuse overFlowLen_ variable for subtraction sum of exponent\n overflowLen_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) + PRECISION; // subtraction part overflowLen_ here: min 65, max 16448\n if (resExponent_ > overflowLen_) {\n resExponent_ = resExponent_ - overflowLen_;\n\n return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_);\n }\n\n // Can happen if bigNumber1 exponent is < 35 (35+16384+29 = 16448) and bigNumber2 exponent is e.g. max 16384.\n // this would mean a branch with a normal big debt factor (bigNumber2) is merged into a base branch with an extremely small\n // debt factor (bigNumber1).\n // this should never happen anyway, but if it does better to revert than to continue with unknown effects.\n revert(); // connection factor should never become a BigNumber with exponent <= 0\n }\n }\n}\n"
|
|
},
|
|
"contracts/libraries/errorTypes.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary LibsErrorTypes {\n /***********************************|\n | LiquidityCalcs | \n |__________________________________*/\n\n /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)\n uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;\n\n /// @notice thrown when rate data is set to a version that is not implemented\n uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;\n\n /***********************************|\n | SafeTransfer | \n |__________________________________*/\n\n /// @notice thrown when safe transfer from for an ERC20 fails\n uint256 internal constant SafeTransfer__TransferFromFailed = 71001;\n\n /// @notice thrown when safe transfer for an ERC20 fails\n uint256 internal constant SafeTransfer__TransferFailed = 71002;\n}\n"
|
|
},
|
|
"contracts/libraries/liquidityCalcs.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { LibsErrorTypes as ErrorTypes } from \"./errorTypes.sol\";\nimport { LiquiditySlotsLink } from \"./liquiditySlotsLink.sol\";\nimport { BigMathMinified } from \"./bigMathMinified.sol\";\n\n/// @notice implements calculation methods used for Fluid liquidity such as updated exchange prices,\n/// borrow rate, withdrawal / borrow limits, revenue amount.\nlibrary LiquidityCalcs {\n error FluidLiquidityCalcsError(uint256 errorId_);\n\n /// @notice emitted if the calculated borrow rate surpassed max borrow rate (16 bits) and was capped at maximum value 65535\n event BorrowRateMaxCap();\n\n /// @dev constants as from Liquidity variables.sol\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev Ignoring leap years\n uint256 internal constant SECONDS_PER_YEAR = 365 days;\n // constants used for BigMath conversion from and to storage\n uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;\n uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;\n\n uint256 internal constant FOUR_DECIMALS = 1e4;\n uint256 internal constant TWELVE_DECIMALS = 1e12;\n uint256 internal constant X14 = 0x3fff;\n uint256 internal constant X15 = 0x7fff;\n uint256 internal constant X16 = 0xffff;\n uint256 internal constant X18 = 0x3ffff;\n uint256 internal constant X24 = 0xffffff;\n uint256 internal constant X33 = 0x1ffffffff;\n uint256 internal constant X64 = 0xffffffffffffffff;\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC EXCHANGE PRICES /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage.\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @return supplyExchangePrice_ updated supplyExchangePrice\n /// @return borrowExchangePrice_ updated borrowExchangePrice\n function calcExchangePrices(\n uint256 exchangePricesAndConfig_\n ) internal view returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {\n // Extracting exchange prices\n supplyExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &\n X64;\n borrowExchangePrice_ =\n (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &\n X64;\n\n if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__ExchangePriceZero);\n }\n\n uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate\n\n unchecked {\n // last timestamp can not be > current timestamp\n uint256 secondsSinceLastUpdate_ = block.timestamp -\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33);\n\n uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) &\n X15;\n if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) {\n // if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed\n // (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // calculate new borrow exchange price.\n // formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_.\n // nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0.\n borrowExchangePrice_ +=\n (borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS);\n\n // FOR SUPPLY EXCHANGE PRICE:\n // all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest.\n // formula: previous price * supply rate * secondsSinceLastUpdate_.\n // where supply rate = (borrow rate - revenueFee%) * ratioSupplyYield. And\n // ratioSupplyYield = utilization * supplyRatio * borrowRatio\n //\n // Example:\n // supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50.\n // BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%.\n // yield is 10 (so half a year must have passed).\n // supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60.\n // borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70.\n // supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2).\n // utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield).\n // borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield):\n // x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%.\n // ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5%\n // supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5%\n // increase in supplyExchangePrice, assuming 100 as previous price.\n // 100 * 22,5% * 1/2 (half a year) = 0,1125.\n // cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20.\n\n // -------------- 1. calculate ratioSupplyYield --------------------------------\n // step1: utilization * supplyRatio (or actually part of lenders receiving yield)\n\n // temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)\n // if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n // else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15;\n\n if (temp_ == 1) {\n // if no raw supply: no exchange price update needed\n // (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0)\n return (supplyExchangePrice_, borrowExchangePrice_);\n }\n\n // ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest\n if (temp_ & 1 == 1) {\n // ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)\n temp_ = temp_ >> 1;\n\n // Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return\n // in the if statement a little above.\n\n // based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee.\n // supplyRawInterest must become worth 30. totalSupply must become 110.\n // supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2.\n // so ratioSupplyYield must come out as 2.5 (250%).\n // supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted.\n temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision)\n // e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%).\n temp_ =\n // utilization * (100% + 100% / supplyRatio)\n (((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) *\n (1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS);\n // max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31\n } else {\n // ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)\n temp_ = temp_ >> 1;\n // if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0\n\n // e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%).\n temp_ =\n // 1e27 * utilization * (100% + supplyRatio) / 100%\n (1e27 *\n ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow).\n (FOUR_DECIMALS + temp_)) /\n (FOUR_DECIMALS * FOUR_DECIMALS);\n // max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27\n }\n // from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31\n\n // step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield)\n if (borrowRatio_ & 1 == 1) {\n // ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n\n // Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered\n // at the beginning of the method by early return if `borrowRatio_ == 1`.\n\n // based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%.\n // so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed.\n // x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666%\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_);\n // max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield).\n } else {\n // ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger)\n borrowRatio_ = borrowRatio_ >> 1;\n\n // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.\n // x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%.\n borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_)));\n // borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%.\n // or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101.\n // max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield).\n }\n\n // temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%.\n // max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8\n temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54;\n\n // 2. calculate supply rate\n // temp_ => supply rate (borrow rate - revenueFee%) * ratioSupplyYield.\n // division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate)\n // Note that all calculation divisions for supplyExchangePrice are rounded down.\n // Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest\n // but more suppliers not earning interest.\n temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate\n temp_ * // ratioSupplyYield\n (FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee\n // fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17.\n\n // 3. calculate increase in supply exchange price\n supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) /\n (SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS));\n // max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0.\n }\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC REVENUE /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage\n /// and the current balance of the Fluid liquidity contract for the token.\n /// @param totalAmounts_ total amounts packed uint256 read from storage\n /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage\n /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))\n /// @return revenueAmount_ collectable revenue amount\n function calcRevenue(\n uint256 totalAmounts_,\n uint256 exchangePricesAndConfig_,\n uint256 liquidityTokenBalance_\n ) internal view returns (uint256 revenueAmount_) {\n // @dev no need to super-optimize this method as it is only used by admin\n\n // calculate the new exchange prices based on earned interest\n (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(exchangePricesAndConfig_);\n\n // total supply = interest free + with interest converted from raw\n uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_);\n\n if (totalSupply_ > 0) {\n // available revenue: balanceOf(token) + totalBorrowings - totalLendings.\n revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_);\n // ensure there is no possible case because of rounding etc. where this would revert,\n // explicitly check if >\n revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0;\n // Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization\n // can only be revenue.\n } else {\n // if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck\n revenueAmount_ = liquidityTokenBalance_;\n }\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC LIMITS /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev calculates withdrawal limit before an operate execution:\n /// amount of user supply that must stay supplied (not amount that can be withdrawn).\n /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M\n /// @param userSupplyData_ user supply data packed uint256 from storage\n /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath\n /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction.\n /// returned value is in raw for with interest mode, normal amount for interest free mode!\n function calcWithdrawalLimitBeforeOperate(\n uint256 userSupplyData_,\n uint256 userSupply_\n ) internal view returns (uint256 currentWithdrawalLimit_) {\n // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet).\n // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet.\n // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be\n // a deposit anyway. Important is that it would not revert.\n\n // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit\n // is the fully expanded limit immediately.\n\n // extract last set withdrawal limit\n uint256 lastWithdrawalLimit_ = (userSupplyData_ >>\n LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) & X64;\n lastWithdrawalLimit_ =\n (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) <<\n (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK);\n if (lastWithdrawalLimit_ == 0) {\n // withdrawal limit is not activated. Max withdrawal allowed\n return 0;\n }\n\n uint256 maxWithdrawableLimit_;\n uint256 temp_;\n unchecked {\n // extract max withdrawable percent of user supply and\n // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed\n // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed.\n\n // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n maxWithdrawableLimit_ =\n (((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) /\n FOUR_DECIMALS;\n\n // time elapsed since last withdrawal limit was set (in seconds)\n // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before.\n // last timestamp can not be > current timestamp\n temp_ =\n block.timestamp -\n ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33);\n }\n // calculate withdrawable amount of expandPercent that is elapsed of expandDuration.\n // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%.\n // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed.\n temp_ =\n (maxWithdrawableLimit_ * temp_) /\n // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit)\n ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0\n // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount.\n // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration,\n // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0\n // which will cause minimum (fully expanded) withdrawal limit to be set in lines below.\n unchecked {\n // underflow explicitly checked & handled\n currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0;\n // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion.\n // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_\n temp_ = userSupply_ - maxWithdrawableLimit_;\n }\n // if withdrawal limit is decreased below minimum then set minimum\n // (e.g. when more than expandDuration time has elapsed)\n if (temp_ > currentWithdrawalLimit_) {\n currentWithdrawalLimit_ = temp_;\n }\n }\n\n /// @dev calculates withdrawal limit after an operate execution:\n /// amount of user supply that must stay supplied (not amount that can be withdrawn).\n /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M\n /// @param userSupplyData_ user supply data packed uint256 from storage\n /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount\n /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate`\n /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in\n /// raw for with interest mode, normal amount for interest free mode!\n function calcWithdrawalLimitAfterOperate(\n uint256 userSupplyData_,\n uint256 userSupply_,\n uint256 newWithdrawalLimit_\n ) internal pure returns (uint256) {\n // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed\n uint256 temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n // if user supply is below base limit then max withdrawals are allowed\n if (userSupply_ < temp_) {\n return 0;\n }\n // temp_ => withdrawal limit expandPercent (is in 1e2 decimals)\n temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;\n unchecked {\n // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent))\n // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_\n temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS);\n }\n // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit.\n // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where\n // increased deposit amount outpaces withrawals.\n if (temp_ > newWithdrawalLimit_) {\n return temp_;\n }\n return newWithdrawalLimit_;\n }\n\n /// @dev calculates borrow limit before an operate execution:\n /// total amount user borrow can reach (not borrowable amount in current operation).\n /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M\n /// @param userBorrowData_ user borrow data packed uint256 from storage\n /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_`\n /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in\n /// raw for with interest mode, normal amount for interest free mode!\n function calcBorrowLimitBeforeOperate(\n uint256 userBorrowData_,\n uint256 userBorrow_\n ) internal view returns (uint256 currentBorrowLimit_) {\n // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit.\n // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus\n // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0.\n\n // temp_ = extract borrow expand percent (is in 1e2 decimals)\n uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;\n\n uint256 maxExpansionLimit_;\n uint256 maxExpandedBorrowLimit_;\n unchecked {\n // calculate max expansion limit: Max amount limit can expand to since last interaction\n // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS);\n\n // calculate max borrow limit: Max point limit can increase to since last interaction\n maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_;\n }\n\n // currentBorrowLimit_ = extract base borrow limit\n currentBorrowLimit_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;\n currentBorrowLimit_ =\n (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) <<\n (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK);\n\n if (maxExpandedBorrowLimit_ < currentBorrowLimit_) {\n return currentBorrowLimit_;\n }\n // time elapsed since last borrow limit was set (in seconds)\n unchecked {\n // temp_ = timeElapsed_ (last timestamp can not be > current timestamp)\n temp_ =\n block.timestamp -\n ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last udpate timestamp\n }\n\n // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit\n currentBorrowLimit_ =\n // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`.\n // divisor is extract expand duration (after this, full expansion to expandPercentage happened).\n ((maxExpansionLimit_ * temp_) /\n ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0\n // extract last set borrow limit\n BigMathMinified.fromBigNumber(\n (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64,\n DEFAULT_EXPONENT_SIZE,\n DEFAULT_EXPONENT_MASK\n );\n\n // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion,\n // so set to `maxExpandedBorrowLimit_` in that case.\n // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big)\n if (currentBorrowLimit_ > maxExpandedBorrowLimit_) {\n currentBorrowLimit_ = maxExpandedBorrowLimit_;\n }\n // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n if (currentBorrowLimit_ > temp_) {\n currentBorrowLimit_ = temp_;\n }\n }\n\n /// @dev calculates borrow limit after an operate execution:\n /// total amount user borrow can reach (not borrowable amount in current operation).\n /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M\n /// @param userBorrowData_ user borrow data packed uint256 from storage\n /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount\n /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate`\n /// @return borrowLimit_ updated borrow limit that should be written to storage.\n /// returned value is in raw for with interest mode, normal amount for interest free mode!\n function calcBorrowLimitAfterOperate(\n uint256 userBorrowData_,\n uint256 userBorrow_,\n uint256 newBorrowLimit_\n ) internal pure returns (uint256 borrowLimit_) {\n // temp_ = extract borrow expand percent\n uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals)\n\n unchecked {\n // borrowLimit_ = calculate maximum borrow limit at full expansion.\n // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).\n borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS);\n }\n\n // temp_ = extract base borrow limit\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n if (borrowLimit_ < temp_) {\n // below base limit, borrow limit is always base limit\n return temp_;\n }\n // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)\n temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;\n temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);\n\n // make sure fully expanded borrow limit is not above hard max borrow limit\n if (borrowLimit_ > temp_) {\n borrowLimit_ = temp_;\n }\n // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit.\n // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)\n if (newBorrowLimit_ > borrowLimit_) {\n return borrowLimit_;\n }\n return newBorrowLimit_;\n }\n\n ///////////////////////////////////////////////////////////////////////////\n ////////// CALC RATES /////////\n ///////////////////////////////////////////////////////////////////////////\n\n /// @dev Calculates new borrow rate from utilization for a token\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ totalBorrow / totalSupply. 1e4 = 100% utilization\n /// @return rate_ rate for that particular token in 1e2 precision (e.g. 5% rate = 500)\n function calcBorrowRateFromUtilization(uint256 rateData_, uint256 utilization_) internal returns (uint256 rate_) {\n // extract rate version: 4 bits (0xF) starting from bit 0\n uint256 rateVersion_ = (rateData_ & 0xF);\n\n if (rateVersion_ == 1) {\n rate_ = calcRateV1(rateData_, utilization_);\n } else if (rateVersion_ == 2) {\n rate_ = calcRateV2(rateData_, utilization_);\n } else {\n revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__UnsupportedRateVersion);\n }\n\n if (rate_ > X16) {\n // hard cap for borrow rate at maximum value 16 bits (65535) to make sure it does not overflow storage space.\n // this is unlikely to ever happen if configs stay within expected levels.\n rate_ = X16;\n // emit event to more easily become aware\n emit BorrowRateMaxCap();\n }\n }\n\n /// @dev calculates the borrow rate based on utilization for rate data version 1 (with one kink) in 1e2 precision\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ in 1e2 (100% = 1e4)\n /// @return rate_ rate in 1e2 precision\n function calcRateV1(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {\n /// For rate v1 (one kink) ------------------------------------------------------\n /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 52- 67 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Last 188 bits => 68-255 => blank, might come in use in future\n\n // y = mx + c.\n // y is borrow rate\n // x is utilization\n // m = slope (m can be 0 but never negative)\n // c is constant (c can be negative)\n\n uint256 y1_;\n uint256 y2_;\n uint256 x1_;\n uint256 x2_;\n\n // extract kink1: 16 bits (0xFFFF) starting from bit 20\n // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two\n uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16;\n if (utilization_ < kink1_) {\n // if utilization is less than kink\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;\n x1_ = 0; // 0%\n x2_ = kink1_;\n } else {\n // else utilization is greater than kink\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) & X16;\n x1_ = kink1_;\n x2_ = FOUR_DECIMALS; // 100%\n }\n\n int256 constant_;\n uint256 slope_;\n unchecked {\n // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).\n // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor) and rate at y2 can not be < rate at y1\n // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS\n slope_ = ((y2_ - y1_) * TWELVE_DECIMALS) / (x2_ - x1_);\n\n // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.\n // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256\n // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;\n // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256\n // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256\n constant_ = int256(y1_ * TWELVE_DECIMALS) - int256(slope_ * x1_);\n\n // calculating new borrow rate\n // - slope_ max value is 65535 * 1e12,\n // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)\n // - constant max value is 65535 * 1e12\n // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256\n // divisor TWELVE_DECIMALS can not be 0\n rate_ = (uint256(int256(slope_ * utilization_) + constant_)) / TWELVE_DECIMALS;\n }\n }\n\n /// @dev calculates the borrow rate based on utilization for rate data version 2 (with two kinks) in 1e4 precision\n /// @param rateData_ rate data packed uint256 from storage for the token\n /// @param utilization_ in 1e2 (100% = 1e4)\n /// @return rate_ rate in 1e4 precision\n function calcRateV2(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {\n /// For rate v2 (two kinks) -----------------------------------------------------\n /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 52- 67 => Utilization at kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 68- 83 => Rate at utilization kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Next 16 bits => 84- 99 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)\n /// Last 156 bits => 100-255 => blank, might come in use in future\n\n // y = mx + c.\n // y is borrow rate\n // x is utilization\n // m = slope (m can be 0 but never negative)\n // c is constant (c can be negative)\n\n uint256 y1_;\n uint256 y2_;\n uint256 x1_;\n uint256 x2_;\n\n // extract kink1: 16 bits (0xFFFF) starting from bit 20\n // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two\n uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) & X16;\n if (utilization_ < kink1_) {\n // if utilization is less than kink1\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;\n x1_ = 0; // 0%\n x2_ = kink1_;\n } else {\n // extract kink2: 16 bits (0xFFFF) starting from bit 52\n uint256 kink2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) & X16;\n if (utilization_ < kink2_) {\n // if utilization is less than kink2\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;\n x1_ = kink1_;\n x2_ = kink2_;\n } else {\n // else utilization is greater than kink2\n y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;\n y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) & X16;\n x1_ = kink2_;\n x2_ = FOUR_DECIMALS;\n }\n }\n\n int256 constant_;\n uint256 slope_;\n unchecked {\n // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).\n // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor) and rate at y2 can not be < rate at y1\n // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS\n slope_ = ((y2_ - y1_) * TWELVE_DECIMALS) / (x2_ - x1_);\n\n // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.\n // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256\n // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;\n // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256\n // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256\n constant_ = int256(y1_ * TWELVE_DECIMALS) - int256(slope_ * x1_);\n\n // calculating new borrow rate\n // - slope_ max value is 65535 * 1e12,\n // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)\n // - constant max value is 65535 * 1e12\n // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256\n // divisor TWELVE_DECIMALS can not be 0\n rate_ = (uint256(int256(slope_ * utilization_) + constant_)) / TWELVE_DECIMALS;\n }\n }\n\n /// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_`\n function getTotalSupply(\n uint256 totalAmounts_,\n uint256 supplyExchangePrice_\n ) internal pure returns (uint256 totalSupply_) {\n // totalSupply_ => supplyInterestFree\n totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;\n totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits\n totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalSupply = supplyInterestFree + supplyRawInterest normalized from raw\n totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n\n /// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_`\n function getTotalBorrow(\n uint256 totalAmounts_,\n uint256 borrowExchangePrice_\n ) internal pure returns (uint256 totalBorrow_) {\n // totalBorrow_ => borrowInterestFree\n // no & mask needed for borrow interest free as it occupies the last bits in the storage slot\n totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);\n totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK);\n\n uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;\n totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK);\n\n // totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw\n totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION);\n }\n}\n"
|
|
},
|
|
"contracts/libraries/liquiditySlotsLink.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @notice library that helps in reading / working with storage slot data of Fluid Liquidity.\n/// @dev as all data for Fluid Liquidity is internal, any data must be fetched directly through manual\n/// slot reading through this library or, if gas usage is less important, through the FluidLiquidityResolver.\nlibrary LiquiditySlotsLink {\n /// @dev storage slot for status at Liquidity\n uint256 internal constant LIQUIDITY_STATUS_SLOT = 1;\n /// @dev storage slot for auths mapping at Liquidity\n uint256 internal constant LIQUIDITY_AUTHS_MAPPING_SLOT = 2;\n /// @dev storage slot for guardians mapping at Liquidity\n uint256 internal constant LIQUIDITY_GUARDIANS_MAPPING_SLOT = 3;\n /// @dev storage slot for user class mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_CLASS_MAPPING_SLOT = 4;\n /// @dev storage slot for exchangePricesAndConfig mapping at Liquidity\n uint256 internal constant LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT = 5;\n /// @dev storage slot for rateData mapping at Liquidity\n uint256 internal constant LIQUIDITY_RATE_DATA_MAPPING_SLOT = 6;\n /// @dev storage slot for totalAmounts mapping at Liquidity\n uint256 internal constant LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT = 7;\n /// @dev storage slot for user supply double mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT = 8;\n /// @dev storage slot for user borrow double mapping at Liquidity\n uint256 internal constant LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT = 9;\n /// @dev storage slot for listed tokens array at Liquidity\n uint256 internal constant LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT = 10;\n\n // --------------------------------\n // @dev stacked uint256 storage slots bits position data for each:\n\n // ExchangePricesAndConfig\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATE = 0;\n uint256 internal constant BITS_EXCHANGE_PRICES_FEE = 16;\n uint256 internal constant BITS_EXCHANGE_PRICES_UTILIZATION = 30;\n uint256 internal constant BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD = 44;\n uint256 internal constant BITS_EXCHANGE_PRICES_LAST_TIMESTAMP = 58;\n uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE = 91;\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE = 155;\n uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_RATIO = 219;\n uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATIO = 234;\n\n // RateData:\n uint256 internal constant BITS_RATE_DATA_VERSION = 0;\n // RateData: V1\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO = 4;\n uint256 internal constant BITS_RATE_DATA_V1_UTILIZATION_AT_KINK = 20;\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK = 36;\n uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX = 52;\n // RateData: V2\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO = 4;\n uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1 = 20;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1 = 36;\n uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2 = 52;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2 = 68;\n uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX = 84;\n\n // TotalAmounts\n uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_WITH_INTEREST = 0;\n uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE = 64;\n uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST = 128;\n uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE = 192;\n\n // UserSupplyData\n uint256 internal constant BITS_USER_SUPPLY_MODE = 0;\n uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;\n uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;\n uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;\n uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;\n uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;\n uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;\n uint256 internal constant BITS_USER_SUPPLY_IS_PAUSED = 255;\n\n // UserBorrowData\n uint256 internal constant BITS_USER_BORROW_MODE = 0;\n uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;\n uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;\n uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;\n uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;\n uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;\n uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;\n uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;\n uint256 internal constant BITS_USER_BORROW_IS_PAUSED = 255;\n\n // --------------------------------\n\n /// @notice Calculating the slot ID for Liquidity contract for single mapping at `slot_` for `key_`\n function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {\n return keccak256(abi.encode(key_, slot_));\n }\n\n /// @notice Calculating the slot ID for Liquidity contract for double mapping at `slot_` for `key1_` and `key2_`\n function calculateDoubleMappingStorageSlot(\n uint256 slot_,\n address key1_,\n address key2_\n ) internal pure returns (bytes32) {\n bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));\n return keccak256(abi.encode(key2_, intermediateSlot_));\n }\n}\n"
|
|
},
|
|
"contracts/libraries/safeTransfer.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT OR Apache-2.0\npragma solidity 0.8.21;\n\nimport { LibsErrorTypes as ErrorTypes } from \"./errorTypes.sol\";\n\n/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom\nlibrary SafeTransfer {\n error FluidSafeTransferError(uint256 errorId_);\n\n /// @dev Transfer `amount_` of `token_` from `from_` to `to_`, spending the approval given by `from_` to the\n /// calling contract. If `token_` returns no value, non-reverting calls are assumed to be successful.\n /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63\n function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"from_\" argument.\n mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"to_\" argument.\n mstore(add(freeMemoryPointer, 68), amount_) // Append the \"amount_\" argument. Masking not required as it's a full 32 byte type.\n\n success_ := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)\n )\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);\n }\n }\n\n /// @dev Transfer `amount_` of `token_` to `to_`.\n /// If `token_` returns no value, non-reverting calls are assumed to be successful.\n /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95\n function safeTransfer(address token_, address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the \"to_\" argument.\n mstore(add(freeMemoryPointer, 36), amount_) // Append the \"amount_\" argument. Masking not required as it's a full 32 byte type.\n\n success_ := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);\n }\n }\n\n /// @dev Transfer `amount_` of ` native token to `to_`.\n /// Minimally modified from Solmate SafeTransferLib (Custom Error):\n /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25\n function safeTransferNative(address to_, uint256 amount_) internal {\n bool success_;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Transfer the ETH and store if it succeeded or not.\n success_ := call(gas(), to_, amount_, 0, 0, 0, 0)\n }\n\n if (!success_) {\n revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);\n }\n }\n}\n"
|
|
},
|
|
"contracts/libraries/storageRead.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @notice implements a method to read uint256 data from storage at a bytes32 storage slot key.\ncontract StorageRead {\n function readFromStorage(bytes32 slot_) public view returns (uint256 result_) {\n assembly {\n result_ := sload(slot_) // read value from the storage slot\n }\n }\n}\n"
|
|
},
|
|
"contracts/libraries/tickMath.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title library that calculates number \"tick\" and \"ratioX96\" from this: ratioX96 = (1.0015^tick) * 2^96\n/// @notice this library is used in Fluid Vault protocol for optimiziation.\n/// @dev \"tick\" supports between -32767 and 32767. \"ratioX96\" supports between 37075072 and 169307877264527972847801929085841449095838922544595\nlibrary TickMath {\n /// The minimum tick that can be passed in getRatioAtTick. 1.0015**-32767\n int24 internal constant MIN_TICK = -32767;\n /// The maximum tick that can be passed in getRatioAtTick. 1.0015**32767\n int24 internal constant MAX_TICK = 32767;\n\n uint256 internal constant FACTOR00 = 0x100000000000000000000000000000000;\n uint256 internal constant FACTOR01 = 0xff9dd7de423466c20352b1246ce4856f; // 2^128/1.0015**1 = 339772707859149738855091969477551883631\n uint256 internal constant FACTOR02 = 0xff3bd55f4488ad277531fa1c725a66d0; // 2^128/1.0015**2 = 339263812140938331358054887146831636176\n uint256 internal constant FACTOR03 = 0xfe78410fd6498b73cb96a6917f853259; // 2^128/1.0015**4 = 338248306163758188337119769319392490073\n uint256 internal constant FACTOR04 = 0xfcf2d9987c9be178ad5bfeffaa123273; // 2^128/1.0015**8 = 336226404141693512316971918999264834163\n uint256 internal constant FACTOR05 = 0xf9ef02c4529258b057769680fc6601b3; // 2^128/1.0015**16 = 332218786018727629051611634067491389875\n uint256 internal constant FACTOR06 = 0xf402d288133a85a17784a411f7aba082; // 2^128/1.0015**32 = 324346285652234375371948336458280706178\n uint256 internal constant FACTOR07 = 0xe895615b5beb6386553757b0352bda90; // 2^128/1.0015**64 = 309156521885964218294057947947195947664\n uint256 internal constant FACTOR08 = 0xd34f17a00ffa00a8309940a15930391a; // 2^128/1.0015**128 = 280877777739312896540849703637713172762 \n uint256 internal constant FACTOR09 = 0xae6b7961714e20548d88ea5123f9a0ff; // 2^128/1.0015**256 = 231843708922198649176471782639349113087\n uint256 internal constant FACTOR10 = 0x76d6461f27082d74e0feed3b388c0ca1; // 2^128/1.0015**512 = 157961477267171621126394973980180876449\n uint256 internal constant FACTOR11 = 0x372a3bfe0745d8b6b19d985d9a8b85bb; // 2^128/1.0015**1024 = 73326833024599564193373530205717235131\n uint256 internal constant FACTOR12 = 0x0be32cbee48979763cf7247dd7bb539d; // 2^128/1.0015**2048 = 15801066890623697521348224657638773661\n uint256 internal constant FACTOR13 = 0x8d4f70c9ff4924dac37612d1e2921e; // 2^128/1.0015**4096 = 733725103481409245883800626999235102\n uint256 internal constant FACTOR14 = 0x4e009ae5519380809a02ca7aec77; // 2^128/1.0015**8192 = 1582075887005588088019997442108535\n uint256 internal constant FACTOR15 = 0x17c45e641b6e95dee056ff10; // 2^128/1.0015**16384 = 7355550435635883087458926352\n\n /// The minimum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MIN_TICK). ~ Equivalent to `(1 << 96) * (1.0015**-32767)`\n uint256 internal constant MIN_RATIOX96 = 37075072;\n /// The maximum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MAX_TICK).\n /// ~ Equivalent to `(1 << 96) * (1.0015**32767)`, rounding etc. leading to minor difference\n uint256 internal constant MAX_RATIOX96 = 169307877264527972847801929085841449095838922544595;\n\n uint256 internal constant ZERO_TICK_SCALED_RATIO = 0x1000000000000000000000000; // 1 << 96 // 79228162514264337593543950336\n uint256 internal constant _1E26 = 1e26;\n\n /// @notice ratioX96 = (1.0015^tick) * 2^96\n /// @dev Throws if |tick| > max tick\n /// @param tick The input tick for the above formula\n /// @return ratioX96 ratio = (debt amount/collateral amount)\n function getRatioAtTick(int tick) internal pure returns (uint256 ratioX96) {\n assembly {\n let absTick_ := sub(xor(tick, sar(255, tick)), sar(255, tick))\n\n if gt(absTick_, MAX_TICK) {\n revert(0, 0)\n }\n let factor_ := FACTOR00\n if and(absTick_, 0x1) {\n factor_ := FACTOR01\n }\n if and(absTick_, 0x2) {\n factor_ := shr(128, mul(factor_, FACTOR02))\n }\n if and(absTick_, 0x4) {\n factor_ := shr(128, mul(factor_, FACTOR03))\n }\n if and(absTick_, 0x8) {\n factor_ := shr(128, mul(factor_, FACTOR04))\n }\n if and(absTick_, 0x10) {\n factor_ := shr(128, mul(factor_, FACTOR05))\n }\n if and(absTick_, 0x20) {\n factor_ := shr(128, mul(factor_, FACTOR06))\n }\n if and(absTick_, 0x40) {\n factor_ := shr(128, mul(factor_, FACTOR07))\n }\n if and(absTick_, 0x80) {\n factor_ := shr(128, mul(factor_, FACTOR08))\n }\n if and(absTick_, 0x100) {\n factor_ := shr(128, mul(factor_, FACTOR09))\n }\n if and(absTick_, 0x200) {\n factor_ := shr(128, mul(factor_, FACTOR10))\n }\n if and(absTick_, 0x400) {\n factor_ := shr(128, mul(factor_, FACTOR11))\n }\n if and(absTick_, 0x800) {\n factor_ := shr(128, mul(factor_, FACTOR12))\n }\n if and(absTick_, 0x1000) {\n factor_ := shr(128, mul(factor_, FACTOR13))\n }\n if and(absTick_, 0x2000) {\n factor_ := shr(128, mul(factor_, FACTOR14))\n }\n if and(absTick_, 0x4000) {\n factor_ := shr(128, mul(factor_, FACTOR15))\n }\n\n let precision_ := 0\n if iszero(and(tick, 0x8000000000000000000000000000000000000000000000000000000000000000)) {\n factor_ := div(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, factor_)\n // we round up in the division so getTickAtRatio of the output price is always consistent\n if mod(factor_, 0x100000000) {\n precision_ := 1\n }\n }\n ratioX96 := add(shr(32, factor_), precision_)\n }\n }\n\n /// @notice ratioX96 = (1.0015^tick) * 2^96\n /// @dev Throws if ratioX96 > max ratio || ratioX96 < min ratio\n /// @param ratioX96 The input ratio; ratio = (debt amount/collateral amount)\n /// @return tick The output tick for the above formula. Returns in round down form. if tick is 123.23 then 123, if tick is -123.23 then returns -124\n /// @return perfectRatioX96 perfect ratio for the above tick\n function getTickAtRatio(uint256 ratioX96) internal pure returns (int tick, uint perfectRatioX96) {\n assembly {\n if or(gt(ratioX96, MAX_RATIOX96), lt(ratioX96, MIN_RATIOX96)) {\n revert(0, 0)\n }\n\n let cond := lt(ratioX96, ZERO_TICK_SCALED_RATIO)\n let factor_\n\n if iszero(cond) {\n // if ratioX96 >= ZERO_TICK_SCALED_RATIO\n factor_ := div(mul(ratioX96, _1E26), ZERO_TICK_SCALED_RATIO)\n }\n if cond {\n // ratioX96 < ZERO_TICK_SCALED_RATIO\n factor_ := div(mul(ZERO_TICK_SCALED_RATIO, _1E26), ratioX96)\n }\n\n // put in https://www.wolframalpha.com/ whole equation: (1.0015^tick) * 2^96 * 10^26 / 79228162514264337593543950336\n\n // for tick = 16384\n // ratioX96 = (1.0015^16384) * 2^96 = 3665252098134783297721995888537077351735\n // 3665252098134783297721995888537077351735 * 10^26 / 79228162514264337593543950336 =\n // 4626198540796508716348404308345255985.06131964639489434655721\n if iszero(lt(factor_, 4626198540796508716348404308345255985)) {\n tick := or(tick, 0x4000)\n factor_ := div(mul(factor_, _1E26), 4626198540796508716348404308345255985)\n }\n // for tick = 8192\n // ratioX96 = (1.0015^8192) * 2^96 = 17040868196391020479062776466509865\n // 17040868196391020479062776466509865 * 10^26 / 79228162514264337593543950336 =\n // 21508599537851153911767490449162.3037648642153898377655505172\n if iszero(lt(factor_, 21508599537851153911767490449162)) {\n tick := or(tick, 0x2000)\n factor_ := div(mul(factor_, _1E26), 21508599537851153911767490449162)\n }\n // for tick = 4096\n // ratioX96 = (1.0015^4096) * 2^96 = 36743933851015821532611831851150\n // 36743933851015821532611831851150 * 10^26 / 79228162514264337593543950336 =\n // 46377364670549310883002866648.9777607649742626173648716941385\n if iszero(lt(factor_, 46377364670549310883002866649)) {\n tick := or(tick, 0x1000)\n factor_ := div(mul(factor_, _1E26), 46377364670549310883002866649)\n }\n // for tick = 2048\n // ratioX96 = (1.0015^2048) * 2^96 = 1706210527034005899209104452335\n // 1706210527034005899209104452335 * 10^26 / 79228162514264337593543950336 =\n // 2153540449365864845468344760.06357108484096046743300420319322\n if iszero(lt(factor_, 2153540449365864845468344760)) {\n tick := or(tick, 0x800)\n factor_ := div(mul(factor_, _1E26), 2153540449365864845468344760)\n }\n // for tick = 1024\n // ratioX96 = (1.0015^1024) * 2^96 = 367668226692760093024536487236\n // 367668226692760093024536487236 * 10^26 / 79228162514264337593543950336 =\n // 464062544207767844008185024.950588990554136265212906454481127\n if iszero(lt(factor_, 464062544207767844008185025)) {\n tick := or(tick, 0x400)\n factor_ := div(mul(factor_, _1E26), 464062544207767844008185025)\n }\n // for tick = 512\n // ratioX96 = (1.0015^512) * 2^96 = 170674186729409605620119663668\n // 170674186729409605620119663668 * 10^26 / 79228162514264337593543950336 =\n // 215421109505955298802281577.031879604792139232258508172947569\n if iszero(lt(factor_, 215421109505955298802281577)) {\n tick := or(tick, 0x200)\n factor_ := div(mul(factor_, _1E26), 215421109505955298802281577)\n }\n // for tick = 256\n // ratioX96 = (1.0015^256) * 2^96 = 116285004205991934861656513301\n // 116285004205991934861656513301 * 10^26 / 79228162514264337593543950336 =\n // 146772309890508740607270614.667650899656438875541505058062410\n if iszero(lt(factor_, 146772309890508740607270615)) {\n tick := or(tick, 0x100)\n factor_ := div(mul(factor_, _1E26), 146772309890508740607270615)\n }\n // for tick = 128\n // ratioX96 = (1.0015^128) * 2^96 = 95984619659632141743747099590\n // 95984619659632141743747099590 * 10^26 / 79228162514264337593543950336 =\n // 121149622323187099817270416.157248837742741760456796835775887\n if iszero(lt(factor_, 121149622323187099817270416)) {\n tick := or(tick, 0x80)\n factor_ := div(mul(factor_, _1E26), 121149622323187099817270416)\n }\n // for tick = 64\n // ratioX96 = (1.0015^64) * 2^96 = 87204845308406958006717891124\n // 87204845308406958006717891124 * 10^26 / 79228162514264337593543950336 =\n // 110067989135437147685980801.568068573422377364214113968609839\n if iszero(lt(factor_, 110067989135437147685980801)) {\n tick := or(tick, 0x40)\n factor_ := div(mul(factor_, _1E26), 110067989135437147685980801)\n }\n // for tick = 32\n // ratioX96 = (1.0015^32) * 2^96 = 83120873769022354029916374475\n // 83120873769022354029916374475 * 10^26 / 79228162514264337593543950336 =\n // 104913292358707887270979599.831816586773651266562785765558183\n if iszero(lt(factor_, 104913292358707887270979600)) {\n tick := or(tick, 0x20)\n factor_ := div(mul(factor_, _1E26), 104913292358707887270979600)\n }\n // for tick = 16\n // ratioX96 = (1.0015^16) * 2^96 = 81151180492336368327184716176\n // 81151180492336368327184716176 * 10^26 / 79228162514264337593543950336 =\n // 102427189924701091191840927.762844039579442328381455567932128\n if iszero(lt(factor_, 102427189924701091191840928)) {\n tick := or(tick, 0x10)\n factor_ := div(mul(factor_, _1E26), 102427189924701091191840928)\n }\n // for tick = 8\n // ratioX96 = (1.0015^8) * 2^96 = 80183906840906820640659903620\n // 80183906840906820640659903620 * 10^26 / 79228162514264337593543950336 =\n // 101206318935480056907421312.890625\n if iszero(lt(factor_, 101206318935480056907421313)) {\n tick := or(tick, 0x8)\n factor_ := div(mul(factor_, _1E26), 101206318935480056907421313)\n }\n // for tick = 4\n // ratioX96 = (1.0015^4) * 2^96 = 79704602139525152702959747603\n // 79704602139525152702959747603 * 10^26 / 79228162514264337593543950336 =\n // 100601351350506250000000000\n if iszero(lt(factor_, 100601351350506250000000000)) {\n tick := or(tick, 0x4)\n factor_ := div(mul(factor_, _1E26), 100601351350506250000000000)\n }\n // for tick = 2\n // ratioX96 = (1.0015^2) * 2^96 = 79466025265172787701084167660\n // 79466025265172787701084167660 * 10^26 / 79228162514264337593543950336 =\n // 100300225000000000000000000\n if iszero(lt(factor_, 100300225000000000000000000)) {\n tick := or(tick, 0x2)\n factor_ := div(mul(factor_, _1E26), 100300225000000000000000000)\n }\n // for tick = 1\n // ratioX96 = (1.0015^1) * 2^96 = 79347004758035734099934266261\n // 79347004758035734099934266261 * 10^26 / 79228162514264337593543950336 =\n // 100150000000000000000000000\n if iszero(lt(factor_, 100150000000000000000000000)) {\n tick := or(tick, 0x1)\n factor_ := div(mul(factor_, _1E26), 100150000000000000000000000)\n }\n if iszero(cond) {\n // if ratioX96 >= ZERO_TICK_SCALED_RATIO\n perfectRatioX96 := div(mul(ratioX96, _1E26), factor_)\n }\n if cond {\n // ratioX96 < ZERO_TICK_SCALED_RATIO\n tick := not(tick)\n perfectRatioX96 := div(mul(ratioX96, factor_), 100150000000000000000000000)\n }\n }\n }\n}\n"
|
|
},
|
|
"contracts/liquidity/adminModule/structs.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nabstract contract Structs {\n struct AddressBool {\n address addr;\n bool value;\n }\n\n struct AddressUint256 {\n address addr;\n uint256 value;\n }\n\n /// @notice struct to set borrow rate data for version 1\n struct RateDataV1Params {\n ///\n /// @param token for rate data\n address token;\n ///\n /// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast\n uint256 kink;\n ///\n /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100\n /// i.e. constant minimum borrow rate\n /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)\n uint256 rateAtUtilizationZero;\n ///\n /// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700\n uint256 rateAtUtilizationKink;\n ///\n /// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500\n uint256 rateAtUtilizationMax;\n }\n\n /// @notice struct to set borrow rate data for version 2\n struct RateDataV2Params {\n ///\n /// @param token for rate data\n address token;\n ///\n /// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster\n uint256 kink1;\n ///\n /// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100\n /// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast\n uint256 kink2;\n ///\n /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100\n /// i.e. constant minimum borrow rate\n /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)\n uint256 rateAtUtilizationZero;\n ///\n /// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700\n uint256 rateAtUtilizationKink1;\n ///\n /// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200\n uint256 rateAtUtilizationKink2;\n ///\n /// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100\n /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500\n uint256 rateAtUtilizationMax;\n }\n\n /// @notice struct to set token config\n struct TokenConfig {\n ///\n /// @param token address\n address token;\n ///\n /// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100\n uint256 fee;\n ///\n /// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100\n uint256 threshold;\n }\n\n /// @notice struct to set user supply & withdrawal config\n struct UserSupplyConfig {\n ///\n /// @param user address\n address user;\n ///\n /// @param token address\n address token;\n ///\n /// @param mode: 0 = without interest. 1 = with interest\n uint8 mode;\n ///\n /// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100\n /// Also used to calculate rate at which withdrawal limit should decrease (instant).\n uint256 expandPercent;\n ///\n /// @param expandDuration withdrawal limit expand duration in seconds.\n /// used to calculate rate together with expandPercent\n uint256 expandDuration;\n ///\n /// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 baseWithdrawalLimit;\n }\n\n /// @notice struct to set user borrow & payback config\n struct UserBorrowConfig {\n ///\n /// @param user address\n address user;\n ///\n /// @param token address\n address token;\n ///\n /// @param mode: 0 = without interest. 1 = with interest\n uint8 mode;\n ///\n /// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100\n /// Also used to calculate rate at which debt limit should decrease (instant).\n uint256 expandPercent;\n ///\n /// @param expandDuration debt limit expand duration in seconds.\n /// used to calculate rate together with expandPercent\n uint256 expandDuration;\n ///\n /// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling\n /// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 baseDebtCeiling;\n ///\n /// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow.\n /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:\n /// with interest -> raw, without interest -> normal\n uint256 maxDebtCeiling;\n }\n}\n"
|
|
},
|
|
"contracts/liquidity/interfaces/iLiquidity.sol": {
|
|
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IProxy } from \"../../infiniteProxy/interfaces/iProxy.sol\";\nimport { Structs as AdminModuleStructs } from \"../adminModule/structs.sol\";\n\ninterface IFluidLiquidityAdmin {\n /// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract.\n /// auths can be helpful in reducing governance overhead where it's not needed.\n /// @param authsStatus_ array of structs setting allowed status for an address.\n /// status true => add auth, false => remove auth\n function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external;\n\n /// @notice adds/removes guardians. Only callable by Governance.\n /// @param guardiansStatus_ array of structs setting allowed status for an address.\n /// status true => add guardian, false => remove guardian\n function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external;\n\n /// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance.\n /// @param revenueCollector_ new revenue collector address\n function updateRevenueCollector(address revenueCollector_) external;\n\n /// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths.\n /// @param newStatus_ new status\n /// status = 2 -> pause, status = 1 -> resume.\n function changeStatus(uint256 newStatus_) external;\n\n /// @notice update tokens rate data version 1. Only callable by Auths.\n /// @param tokensRateData_ array of RateDataV1Params with rate data to set for each token\n function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external;\n\n /// @notice update tokens rate data version 2. Only callable by Auths.\n /// @param tokensRateData_ array of RateDataV2Params with rate data to set for each token\n function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external;\n\n /// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold.\n /// Only callable by Auths.\n /// @param tokenConfigs_ contains token address, fee & utilization threshold\n function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external;\n\n /// @notice updates user classes: 0 is for new protocols, 1 is for established protocols.\n /// Only callable by Auths.\n /// @param userClasses_ struct array of uint256 value to assign for each user address\n function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external;\n\n /// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits.\n /// Only callable by Auths.\n /// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info\n function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external;\n\n /// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits.\n /// Only callable by Auths.\n /// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info\n function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external;\n\n /// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians).\n /// Only callable by Guardians.\n /// @param user_ address of user to pause operations for\n /// @param supplyTokens_ token addresses to pause withdrawals for\n /// @param borrowTokens_ token addresses to pause borrowings for\n function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;\n\n /// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians).\n /// Only callable by Guardians.\n /// @param user_ address of user to unpause operations for\n /// @param supplyTokens_ token addresses to unpause withdrawals for\n /// @param borrowTokens_ token addresses to unpause borrowings for\n function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;\n\n /// @notice collects revenue for tokens to configured revenueCollector address.\n /// @param tokens_ array of tokens to collect revenue for\n /// @dev Note that this can revert if token balance is < revenueAmount (utilization > 100%)\n function collectRevenue(address[] calldata tokens_) external;\n\n /// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage.\n /// @param tokens_ tokens to update exchange prices for\n /// @return supplyExchangePrices_ new supply rates of overall system for each token\n /// @return borrowExchangePrices_ new borrow rates of overall system for each token\n function updateExchangePrices(\n address[] calldata tokens_\n ) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_);\n}\n\ninterface IFluidLiquidityLogic is IFluidLiquidityAdmin {\n /// @notice Single function which handles supply, withdraw, borrow & payback\n /// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native)\n /// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing\n /// @param borrowAmount_ if +ve then borrow, if -ve then payback, if 0 then nothing\n /// @param withdrawTo_ if withdrawal then to which address\n /// @param borrowTo_ if borrow then to which address\n /// @param callbackData_ callback data passed to `liquidityCallback` method of protocol\n /// @return memVar3_ updated supplyExchangePrice\n /// @return memVar4_ updated borrowExchangePrice\n /// @dev to trigger skipping in / out transfers when in&out amounts balance themselves out (gas optimization):\n /// - supply(+) == borrow(+), withdraw(-) == payback(-).\n /// - `withdrawTo_` / `borrowTo_` must be msg.sender (protocol)\n /// - `callbackData_` MUST be encoded so that \"from\" address is at last 20 bytes (if this optimization is desired),\n /// also for native token operations where liquidityCallback is not triggered!\n /// from address must come at last position if there is more data. I.e. encode like:\n /// abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end\n /// so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end.\n function operate(\n address token_,\n int256 supplyAmount_,\n int256 borrowAmount_,\n address withdrawTo_,\n address borrowTo_,\n bytes calldata callbackData_\n ) external payable returns (uint256 memVar3_, uint256 memVar4_);\n}\n\ninterface IFluidLiquidity is IProxy, IFluidLiquidityLogic {}\n"
|
|
},
|
|
"contracts/oracle/fluidOracle.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidOracle } from \"./interfaces/iFluidOracle.sol\";\n\n/// @title FluidOracle\n/// @notice Base contract that any Fluid Oracle must implement\nabstract contract FluidOracle is IFluidOracle {\n /// @inheritdoc IFluidOracle\n function getExchangeRate() external view virtual returns (uint256 exchangeRate_);\n}\n"
|
|
},
|
|
"contracts/oracle/interfaces/iFluidOracle.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidOracle {\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27\n function getExchangeRate() external view returns (uint256 exchangeRate_);\n}\n"
|
|
},
|
|
"contracts/protocols/vault/error.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Error {\n error FluidVaultError(uint256 errorId_);\n\n /// @notice used to simulate liquidation to find the maximum liquidatable amounts\n error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated);\n}\n"
|
|
},
|
|
"contracts/protocols/vault/errorTypes.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | Vault Factory | \n |__________________________________*/\n\n uint256 internal constant VaultFactory__InvalidOperation = 30001;\n uint256 internal constant VaultFactory__Unauthorized = 30002;\n uint256 internal constant VaultFactory__SameTokenNotAllowed = 30003;\n uint256 internal constant VaultFactory__InvalidParams = 30004;\n uint256 internal constant VaultFactory__InvalidVault = 30005;\n uint256 internal constant VaultFactory__InvalidVaultAddress = 30006;\n uint256 internal constant VaultFactory__OnlyDelegateCallAllowed = 30007;\n\n /***********************************|\n | VaultT1 | \n |__________________________________*/\n\n /// @notice thrown at reentrancy\n uint256 internal constant VaultT1__AlreadyEntered = 31001;\n\n /// @notice thrown when user sends deposit & borrow amount as 0\n uint256 internal constant VaultT1__InvalidOperateAmount = 31002;\n\n /// @notice thrown when msg.value is not in sync with native token deposit or payback\n uint256 internal constant VaultT1__InvalidMsgValueOperate = 31003;\n\n /// @notice thrown when msg.sender is not the owner of the vault\n uint256 internal constant VaultT1__NotAnOwner = 31004;\n\n /// @notice thrown when user's position does not exist. Sending the wrong index from the frontend\n uint256 internal constant VaultT1__TickIsEmpty = 31005;\n\n /// @notice thrown when the user's position is above CF and the user tries to make it more risky by trying to withdraw or borrow\n uint256 internal constant VaultT1__PositionAboveCF = 31006;\n\n /// @notice thrown when the top tick is not initialized. Happens if the vault is totally new or all the user's left\n uint256 internal constant VaultT1__TopTickDoesNotExist = 31007;\n\n /// @notice thrown when msg.value in liquidate is not in sync payback\n uint256 internal constant VaultT1__InvalidMsgValueLiquidate = 31008;\n\n /// @notice thrown when slippage is more on liquidation than what the liquidator sent\n uint256 internal constant VaultT1__ExcessSlippageLiquidation = 31009;\n\n /// @notice thrown when msg.sender is not the rebalancer/reserve contract\n uint256 internal constant VaultT1__NotRebalancer = 31010;\n\n /// @notice thrown when NFT of one vault interacts with the NFT of other vault\n uint256 internal constant VaultT1__NftNotOfThisVault = 31011;\n\n /// @notice thrown when the token is not initialized on the liquidity contract\n uint256 internal constant VaultT1__TokenNotInitialized = 31012;\n\n /// @notice thrown when admin updates fallback if a non-auth calls vault\n uint256 internal constant VaultT1__NotAnAuth = 31013;\n\n /// @notice thrown in operate when user tries to witdhraw more collateral than deposited\n uint256 internal constant VaultT1__ExcessCollateralWithdrawal = 31014;\n\n /// @notice thrown in operate when user tries to payback more debt than borrowed\n uint256 internal constant VaultT1__ExcessDebtPayback = 31015;\n\n /// @notice thrown when user try to withdrawal more than operate's withdrawal limit\n uint256 internal constant VaultT1__WithdrawMoreThanOperateLimit = 31016;\n\n /// @notice thrown when caller of liquidityCallback is not Liquidity\n uint256 internal constant VaultT1__InvalidLiquidityCallbackAddress = 31017;\n\n /// @notice thrown when reentrancy is not already on\n uint256 internal constant VaultT1__NotEntered = 31018;\n\n /// @notice thrown when someone directly calls secondary implementation contract\n uint256 internal constant VaultT1__OnlyDelegateCallAllowed = 31019;\n\n /// @notice thrown when the safeTransferFrom for a token amount failed\n uint256 internal constant VaultT1__TransferFromFailed = 31020;\n\n /// @notice thrown when exchange price overflows while updating on storage\n uint256 internal constant VaultT1__ExchangePriceOverFlow = 31021;\n\n /// @notice thrown when debt to liquidate amt is sent wrong\n uint256 internal constant VaultT1__InvalidLiquidationAmt = 31022;\n\n /// @notice thrown when user debt or collateral goes above 2**128 or below -2**128\n uint256 internal constant VaultT1__UserCollateralDebtExceed = 31023;\n\n /// @notice thrown if on liquidation branch debt becomes lower than 100\n uint256 internal constant VaultT1__BranchDebtTooLow = 31024;\n\n /// @notice thrown when tick's debt is less than 10000\n uint256 internal constant VaultT1__TickDebtTooLow = 31025;\n\n /// @notice thrown when the received new liquidity exchange price is of unexpected value (< than the old one)\n uint256 internal constant VaultT1__LiquidityExchangePriceUnexpected = 31026;\n\n /// @notice thrown when user's debt is less than 10000\n uint256 internal constant VaultT1__UserDebtTooLow = 31027;\n\n /// @notice thrown when on only payback and only deposit the ratio of position increases\n uint256 internal constant VaultT1__InvalidPaybackOrDeposit = 31028;\n\n /***********************************|\n | ERC721 | \n |__________________________________*/\n\n uint256 internal constant ERC721__InvalidParams = 32001;\n uint256 internal constant ERC721__Unauthorized = 32002;\n uint256 internal constant ERC721__InvalidOperation = 32003;\n uint256 internal constant ERC721__UnsafeRecipient = 32004;\n uint256 internal constant ERC721__OutOfBoundsIndex = 32005;\n\n /***********************************|\n | Vault Admin | \n |__________________________________*/\n\n /// @notice thrown when admin tries to setup invalid value which are crossing limits\n uint256 internal constant VaultT1Admin__ValueAboveLimit = 33001;\n\n /// @notice when someone directly calls admin implementation contract\n uint256 internal constant VaultT1Admin__OnlyDelegateCallAllowed = 33002;\n\n /// @notice thrown when auth sends NFT ID as 0 while collecting dust debt\n uint256 internal constant VaultT1Admin__NftIdShouldBeNonZero = 33003;\n\n /// @notice thrown when trying to collect dust debt of NFT which is not of this vault\n uint256 internal constant VaultT1Admin__NftNotOfThisVault = 33004;\n\n /// @notice thrown when dust debt of NFT is 0, meaning nothing to collect\n uint256 internal constant VaultT1Admin__DustDebtIsZero = 33005;\n\n /// @notice thrown when final debt after liquidation is not 0, meaning position 100% liquidated\n uint256 internal constant VaultT1Admin__FinalDebtShouldBeZero = 33006;\n\n /// @notice thrown when NFT is not liquidated state\n uint256 internal constant VaultT1Admin__NftNotLiquidated = 33007;\n\n /// @notice thrown when total absorbed dust debt is 0\n uint256 internal constant VaultT1Admin__AbsorbedDustDebtIsZero = 33008;\n\n /// @notice thrown when address is set as 0\n uint256 internal constant VaultT1Admin__AddressZeroNotAllowed = 33009;\n\n /***********************************|\n | Vault Rewards | \n |__________________________________*/\n\n uint256 internal constant VaultRewards__Unauthorized = 34001;\n uint256 internal constant VaultRewards__AddressZero = 34002;\n uint256 internal constant VaultRewards__InvalidParams = 34003;\n uint256 internal constant VaultRewards__NewMagnifierSameAsOldMagnifier = 34004;\n uint256 internal constant VaultRewards__NotTheInitiator = 34005;\n uint256 internal constant VaultRewards__AlreadyStarted = 34006;\n uint256 internal constant VaultRewards__RewardsNotStartedOrEnded = 34007;\n}\n"
|
|
},
|
|
"contracts/protocols/vault/factory/deploymentLogics/vaultT1Logic.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { SSTORE2 } from \"solmate/src/utils/SSTORE2.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { Error } from \"../../error.sol\";\nimport { IFluidVaultFactory } from \"../../interfaces/iVaultFactory.sol\";\n\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\n\nimport { IFluidVaultT1 } from \"../../interfaces/iVaultT1.sol\";\nimport { FluidVaultT1 } from \"../../vaultT1/coreModule/main.sol\";\n\ninterface IERC20 {\n function decimals() external view returns (uint8);\n}\n\ncontract FluidVaultT1DeploymentLogic is Error {\n address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n /// @notice SSTORE2 pointer for the VaultT1 creation code. Stored externally to reduce factory bytecode\n address public immutable VAULT_T1_CREATIONCODE_ADDRESS;\n\n /// @notice address of liquidity contract\n address public immutable LIQUIDITY;\n\n /// @notice address of Admin implementation\n address public immutable ADMIN_IMPLEMENTATION;\n\n /// @notice address of Secondary implementation\n address public immutable SECONDARY_IMPLEMENTATION;\n\n /// @notice address of this contract\n address public immutable ADDRESS_THIS;\n\n /// @notice Emitted when a new vaultT1 is deployed.\n /// @param vault The address of the newly deployed vault.\n /// @param vaultId The id of the newly deployed vault.\n /// @param supplyToken The address of the supply token.\n /// @param borrowToken The address of the borrow token.\n event VaultT1Deployed(\n address indexed vault,\n uint256 vaultId,\n address indexed supplyToken,\n address indexed borrowToken\n );\n\n constructor(address liquidity_, address vaultAdminImplementation_, address vaultSecondaryImplementation_) {\n LIQUIDITY = liquidity_;\n ADMIN_IMPLEMENTATION = vaultAdminImplementation_;\n SECONDARY_IMPLEMENTATION = vaultSecondaryImplementation_;\n VAULT_T1_CREATIONCODE_ADDRESS = SSTORE2.write(type(FluidVaultT1).creationCode);\n ADDRESS_THIS = address(this);\n }\n\n /// @dev Calculates the liquidity vault slots for the given supply token, borrow token, and vault (`vault_`).\n /// @param constants_ Constants struct as used in Vault T1\n /// @param vault_ The address of the vault.\n /// @return liquidityVaultSlots_ Returns the calculated liquidity vault slots set in the `IFluidVaultT1.ConstantViews` struct.\n function _calculateLiquidityVaultSlots(\n IFluidVaultT1.ConstantViews memory constants_,\n address vault_\n ) private pure returns (IFluidVaultT1.ConstantViews memory) {\n constants_.liquidityTotalSupplySlot = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n constants_.supplyToken\n );\n constants_.liquidityTotalBorrowSlot = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT,\n constants_.borrowToken\n );\n constants_.liquiditySupplyExchangePriceSlot = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n constants_.supplyToken\n );\n constants_.liquidityBorrowExchangePriceSlot = LiquiditySlotsLink.calculateMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,\n constants_.borrowToken\n );\n constants_.liquidityUserSupplySlot = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT,\n vault_,\n constants_.supplyToken\n );\n constants_.liquidityUserBorrowSlot = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(\n LiquiditySlotsLink.LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT,\n vault_,\n constants_.borrowToken\n );\n return constants_;\n }\n\n /// @notice Computes vaultT1 bytecode for the given supply token (`supplyToken_`) and borrow token (`borrowToken_`).\n /// This will be called by the VaultFactory via .delegateCall\n /// @param supplyToken_ The address of the supply token.\n /// @param borrowToken_ The address of the borrow token.\n /// @return vaultCreationBytecode_ Returns the bytecode of the new vault to deploy.\n function vaultT1(\n address supplyToken_,\n address borrowToken_\n ) external returns (bytes memory vaultCreationBytecode_) {\n if (address(this) == ADDRESS_THIS) revert FluidVaultError(ErrorTypes.VaultFactory__OnlyDelegateCallAllowed);\n\n if (supplyToken_ == borrowToken_) revert FluidVaultError(ErrorTypes.VaultFactory__SameTokenNotAllowed);\n\n IFluidVaultT1.ConstantViews memory constants_;\n constants_.liquidity = LIQUIDITY;\n constants_.factory = address(this);\n constants_.adminImplementation = ADMIN_IMPLEMENTATION;\n constants_.secondaryImplementation = SECONDARY_IMPLEMENTATION;\n constants_.supplyToken = supplyToken_;\n constants_.supplyDecimals = supplyToken_ != NATIVE_TOKEN ? IERC20(supplyToken_).decimals() : 18;\n constants_.borrowToken = borrowToken_;\n constants_.borrowDecimals = borrowToken_ != NATIVE_TOKEN ? IERC20(borrowToken_).decimals() : 18;\n constants_.vaultId = IFluidVaultFactory(address(this)).totalVaults();\n\n address vault_ = IFluidVaultFactory(address(this)).getVaultAddress(constants_.vaultId);\n\n constants_ = _calculateLiquidityVaultSlots(constants_, vault_);\n\n vaultCreationBytecode_ = abi.encodePacked(SSTORE2.read(VAULT_T1_CREATIONCODE_ADDRESS), abi.encode(constants_));\n\n emit VaultT1Deployed(vault_, constants_.vaultId, supplyToken_, borrowToken_);\n\n return vaultCreationBytecode_;\n }\n}\n"
|
|
},
|
|
"contracts/protocols/vault/interfaces/iVaultFactory.sol": {
|
|
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IERC721Enumerable } from \"@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol\";\n\ninterface IFluidVaultFactory is IERC721Enumerable {\n /// @notice Minting an NFT Vault for the user\n function mint(uint256 vaultId_, address user_) external returns (uint256 tokenId_);\n\n /// @notice returns owner of Vault which is also an NFT\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /// @notice Global auth is auth for all vaults\n function isGlobalAuth(address auth_) external view returns (bool);\n\n /// @notice Vault auth is auth for a specific vault\n function isVaultAuth(address auth_, address vault_) external view returns (bool);\n\n /// @notice Total vaults deployed.\n function totalVaults() external view returns (uint256);\n\n /// @notice Compute vaultAddress\n function getVaultAddress(uint256 vaultId) external view returns (address);\n\n /// @notice read uint256 `result_` for a storage `slot_` key\n function readFromStorage(bytes32 slot_) external view returns (uint256 result_);\n}\n"
|
|
},
|
|
"contracts/protocols/vault/interfaces/iVaultT1.sol": {
|
|
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidVaultT1 {\n /// @notice returns the vault id\n function VAULT_ID() external view returns (uint256);\n\n /// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key.\n function readFromStorage(bytes32 slot_) external view returns (uint256 result_);\n\n struct ConstantViews {\n address liquidity;\n address factory;\n address adminImplementation;\n address secondaryImplementation;\n address supplyToken;\n address borrowToken;\n uint8 supplyDecimals;\n uint8 borrowDecimals;\n uint vaultId;\n bytes32 liquidityTotalSupplySlot;\n bytes32 liquidityTotalBorrowSlot;\n bytes32 liquiditySupplyExchangePriceSlot;\n bytes32 liquidityBorrowExchangePriceSlot;\n bytes32 liquidityUserSupplySlot;\n bytes32 liquidityUserBorrowSlot;\n }\n\n /// @notice returns all Vault constants\n function constantsView() external view returns (ConstantViews memory constantsView_);\n\n /// @notice fetches the latest user position after a liquidation\n function fetchLatestPosition(\n int256 positionTick_,\n uint256 positionTickId_,\n uint256 positionRawDebt_,\n uint256 tickData_\n )\n external\n view\n returns (\n int256, // tick\n uint256, // raw debt\n uint256, // raw collateral\n uint256, // branchID_\n uint256 // branchData_\n );\n\n /// @notice calculates the updated vault exchange prices\n function updateExchangePrices(\n uint256 vaultVariables2_\n )\n external\n view\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n );\n\n /// @notice calculates the updated vault exchange prices and writes them to storage\n function updateExchangePricesOnStorage()\n external\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n );\n\n /// @notice returns the liquidity contract address\n function LIQUIDITY() external view returns (address);\n\n function operate(\n uint256 nftId_, // if 0 then new position\n int256 newCol_, // if negative then withdraw\n int256 newDebt_, // if negative then payback\n address to_ // address at which the borrow & withdraw amount should go to. If address(0) then it'll go to msg.sender\n )\n external\n payable\n returns (\n uint256, // nftId_\n int256, // final supply amount. if - then withdraw\n int256 // final borrow amount. if - then payback\n );\n \n function liquidate(\n uint256 debtAmt_,\n uint256 colPerUnitDebt_, // min collateral needed per unit of debt in 1e18\n address to_,\n bool absorb_\n ) external payable returns (uint actualDebtAmt_, uint actualColAmt_);\n\n function absorb() external;\n\n function rebalance() external payable returns (int supplyAmt_, int borrowAmt_);\n\n error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated);\n}\n"
|
|
},
|
|
"contracts/protocols/vault/vaultT1/common/variables.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Variables {\n /***********************************|\n | Storage Variables |\n |__________________________________*/\n\n /// note: in all variables. For tick >= 0 are represented with bit as 1, tick < 0 are represented with bit as 0\n /// note: read all the variables through storageRead.sol\n\n /// note: vaultVariables contains vault variables which need regular updates through transactions\n /// First 1 bit => 0 => re-entrancy. If 0 then allow transaction to go, else throw.\n /// Next 1 bit => 1 => Is the current active branch liquidated? If true then check the branch's minima tick before creating a new position\n /// If the new tick is greater than minima tick then initialize a new branch, make that as current branch & do proper linking\n /// Next 1 bit => 2 => sign of topmost tick (0 -> negative; 1 -> positive)\n /// Next 19 bits => 3-21 => absolute value of topmost tick\n /// Next 30 bits => 22-51 => current branch ID\n /// Next 30 bits => 52-81 => total branch ID\n /// Next 64 bits => 82-145 => Total supply\n /// Next 64 bits => 146-209 => Total borrow\n /// Next 32 bits => 210-241 => Total positions\n uint256 internal vaultVariables;\n\n /// note: vaultVariables2 contains variables which do not update on every transaction. So mainly admin/auth set amount\n /// First 16 bits => 0-15 => supply rate magnifier; 10000 = 1x (Here 16 bits should be more than enough)\n /// Next 16 bits => 16-31 => borrow rate magnifier; 10000 = 1x (Here 16 bits should be more than enough)\n /// Next 10 bits => 32-41 => collateral factor. 800 = 0.8 = 80% (max precision of 0.1%)\n /// Next 10 bits => 42-51 => liquidation Threshold. 900 = 0.9 = 90% (max precision of 0.1%)\n /// Next 10 bits => 52-61 => liquidation Max Limit. 950 = 0.95 = 95% (max precision of 0.1%) (above this 100% liquidation can happen)\n /// Next 10 bits => 62-71 => withdraw gap. 100 = 0.1 = 10%. (max precision of 0.1%) (max 7 bits can also suffice for the requirement here of 0.1% to 10%). Needed to save some limits on withdrawals so liquidate can work seamlessly.\n /// Next 10 bits => 72-81 => liquidation penalty. 100 = 0.01 = 1%. (max precision of 0.01%) (max liquidation penantly can be 10.23%). Applies when tick is in between liquidation Threshold & liquidation Max Limit.\n /// Next 10 bits => 82-91 => borrow fee. 100 = 0.01 = 1%. (max precision of 0.01%) (max borrow fee can be 10.23%). Fees on borrow.\n /// Next 4 bits => 92-95 => empty\n /// Next 160 bits => 96-255 => Oracle address\n uint256 internal vaultVariables2;\n\n /// note: stores absorbed liquidity\n /// First 128 bits raw debt amount\n /// last 128 bits raw col amount\n uint256 internal absorbedLiquidity;\n\n /// position index => position data uint\n /// if the entire variable is 0 (meaning not initialized) at the start that means no position at all\n /// First 1 bit => 0 => position type (0 => borrow position; 1 => supply position)\n /// Next 1 bit => 1 => sign of user's tick (0 => negative; 1 => positive)\n /// Next 19 bits => 2-20 => absolute value of user's tick\n /// Next 24 bits => 21-44 => user's tick's id\n /// Below we are storing user's collateral & not debt, because the position can also be only collateral with no tick but it can never be only debt\n /// Next 64 bits => 45-108 => user's supply amount. Debt will be calculated through supply & ratio.\n /// Next 64 bits => 109-172 => user's dust debt amount. User's net debt = total debt - dust amount. Total debt is calculated through supply & ratio\n /// User won't pay any extra interest on dust debt & hence we will not show it as a debt on UI. For user's there's no dust.\n mapping(uint256 => uint256) internal positionData;\n\n /// Tick has debt only keeps data of non liquidated positions. liquidated tick's data stays in branch itself\n /// tick parent => uint (represents bool for 256 children)\n /// parent of (i)th tick:-\n /// if (i>=0) (i / 256);\n /// else ((i + 1) / 256) - 1\n /// first bit of the variable is the smallest tick & last bit is the biggest tick of that slot\n mapping(int256 => uint256) internal tickHasDebt;\n\n /// mapping tickId => tickData\n /// Tick related data. Total debt & other things\n /// First bit => 0 => If 1 then liquidated else not liquidated\n /// Next 24 bits => 1-24 => Total IDs. ID should start from 1.\n /// If not liquidated:\n /// Next 64 bits => 25-88 => raw debt\n /// If liquidated\n /// The below 3 things are of last ID. This is to be updated when user creates a new position\n /// Next 1 bit => 25 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated (100% liquidated)\n /// Next 30 bits => 26-55 => branch ID where this tick got liquidated\n /// Next 50 bits => 56-105 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n mapping(int256 => uint256) internal tickData;\n\n /// tick id => previous tick id liquidation data. ID starts from 1\n /// One tick ID contains 3 IDs of 80 bits in it, holding liquidation data of previously active but liquidated ticks\n /// 81 bits data below\n /// #### First 85 bits ####\n /// 1st bit => 0 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 1-30 => branch ID where this tick got liquidated\n /// Next 50 bits => 31-80 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n /// #### Second 85 bits ####\n /// 85th bit => 85 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 86-115 => branch ID where this tick got liquidated\n /// Next 50 bits => 116-165 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n /// #### Third 85 bits ####\n /// 170th bit => 170 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated\n /// Next 30 bits => 171-200 => branch ID where this tick got liquidated\n /// Next 50 bits => 201-250 => debt factor 50 bits (35 bits coefficient | 15 bits expansion)\n mapping(int256 => mapping(uint256 => uint256)) internal tickId;\n\n /// mapping branchId => branchData\n /// First 2 bits => 0-1 => if 0 then not liquidated, if 1 then liquidated, if 2 then merged, if 3 then closed\n /// merged means the branch is merged into it's base branch\n /// closed means all the users are 100% liquidated\n /// Next 1 bit => 2 => minima tick sign of this branch. Will only be there if any liquidation happened.\n /// Next 19 bits => 3-21 => minima tick of this branch. Will only be there if any liquidation happened.\n /// Next 30 bits => 22-51 => Partials of minima tick of branch this is connected to. 0 if master branch.\n /// Next 64 bits => 52-115 Debt liquidity at this branch. Similar to last's top tick data. Remaining debt will move here from tickData after first liquidation\n /// If not merged\n /// Next 50 bits => 116-165 => Debt factor or of this branch. (35 bits coefficient | 15 bits expansion)\n /// If merged\n /// Next 50 bits => 116-165 => Connection/adjustment debt factor of this branch with the next branch.\n /// If closed\n /// Next 50 bits => 116-165 => Debt factor as 0. As all the user's positions are now fully gone\n /// following values are present always again (merged / not merged / closed)\n /// Next 30 bits => 166-195 => Branch's ID with which this branch is connected. If 0 then that means this is the master branch\n /// Next 1 bit => 196 => sign of minima tick of branch this is connected to. 0 if master branch.\n /// Next 19 bits => 197-215 => minima tick of branch this is connected to. 0 if master branch.\n mapping(uint256 => uint256) internal branchData;\n\n /// Exchange prices are in 1e12\n /// First 64 bits => 0-63 => Liquidity's collateral token supply exchange price\n /// First 64 bits => 64-127 => Liquidity's debt token borrow exchange price\n /// First 64 bits => 128-191 => Vault's collateral token supply exchange price\n /// First 64 bits => 192-255 => Vault's debt token borrow exchange price\n uint256 internal rates;\n\n /// address of rebalancer\n address internal rebalancer;\n\n uint256 internal absorbedDustDebt;\n}\n"
|
|
},
|
|
"contracts/protocols/vault/vaultT1/coreModule/constantVariables.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidVaultFactory } from \"../../interfaces/iVaultFactory.sol\";\nimport { IFluidLiquidity } from \"../../../../liquidity/interfaces/iLiquidity.sol\";\nimport { StorageRead } from \"../../../../libraries/storageRead.sol\";\n\nimport { Structs } from \"./structs.sol\";\n\ninterface TokenInterface {\n function decimals() external view returns (uint8);\n}\n\ncontract ConstantVariables is StorageRead, Structs {\n /***********************************|\n | Constant Variables |\n |__________________________________*/\n\n address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n /// @dev collateral token address\n address internal immutable SUPPLY_TOKEN;\n /// @dev borrow token address\n address internal immutable BORROW_TOKEN;\n\n /// @dev Token decimals. For example wETH is 18 decimals\n uint8 internal immutable SUPPLY_DECIMALS;\n /// @dev Token decimals. For example USDC is 6 decimals\n uint8 internal immutable BORROW_DECIMALS;\n\n /// @dev VaultT1 AdminModule implemenation address\n address internal immutable ADMIN_IMPLEMENTATION;\n\n /// @dev VaultT1 Secondary implemenation (main2.sol) address\n address internal immutable SECONDARY_IMPLEMENTATION;\n\n /// @dev liquidity proxy contract address\n IFluidLiquidity public immutable LIQUIDITY;\n\n /// @dev vault factory contract address\n IFluidVaultFactory public immutable VAULT_FACTORY;\n\n uint public immutable VAULT_ID;\n\n uint internal constant X8 = 0xff;\n uint internal constant X10 = 0x3ff;\n uint internal constant X16 = 0xffff;\n uint internal constant X19 = 0x7ffff;\n uint internal constant X20 = 0xfffff;\n uint internal constant X24 = 0xffffff;\n uint internal constant X25 = 0x1ffffff;\n uint internal constant X30 = 0x3fffffff;\n uint internal constant X35 = 0x7ffffffff;\n uint internal constant X50 = 0x3ffffffffffff;\n uint internal constant X64 = 0xffffffffffffffff;\n uint internal constant X96 = 0xffffffffffffffffffffffff;\n uint internal constant X128 = 0xffffffffffffffffffffffffffffffff;\n\n uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;\n\n /// @dev slot ids in Liquidity contract. Helps in low gas fetch from liquidity contract by skipping delegate call\n bytes32 internal immutable LIQUIDITY_TOTAL_SUPPLY_SLOT;\n bytes32 internal immutable LIQUIDITY_TOTAL_BORROW_SLOT;\n bytes32 internal immutable LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT;\n bytes32 internal immutable LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT;\n bytes32 internal immutable LIQUIDITY_USER_SUPPLY_SLOT;\n bytes32 internal immutable LIQUIDITY_USER_BORROW_SLOT;\n\n /// @notice returns all Vault constants\n function constantsView() external view returns (ConstantViews memory constantsView_) {\n constantsView_.liquidity = address(LIQUIDITY);\n constantsView_.factory = address(VAULT_FACTORY);\n constantsView_.adminImplementation = ADMIN_IMPLEMENTATION;\n constantsView_.secondaryImplementation = SECONDARY_IMPLEMENTATION;\n constantsView_.supplyToken = SUPPLY_TOKEN;\n constantsView_.borrowToken = BORROW_TOKEN;\n constantsView_.supplyDecimals = SUPPLY_DECIMALS;\n constantsView_.borrowDecimals = BORROW_DECIMALS;\n constantsView_.vaultId = VAULT_ID;\n constantsView_.liquidityTotalSupplySlot = LIQUIDITY_TOTAL_SUPPLY_SLOT;\n constantsView_.liquidityTotalBorrowSlot = LIQUIDITY_TOTAL_BORROW_SLOT;\n constantsView_.liquiditySupplyExchangePriceSlot = LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT;\n constantsView_.liquidityBorrowExchangePriceSlot = LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT;\n constantsView_.liquidityUserSupplySlot = LIQUIDITY_USER_SUPPLY_SLOT;\n constantsView_.liquidityUserBorrowSlot = LIQUIDITY_USER_BORROW_SLOT;\n }\n\n constructor(ConstantViews memory constants_) {\n LIQUIDITY = IFluidLiquidity(constants_.liquidity);\n VAULT_FACTORY = IFluidVaultFactory(constants_.factory);\n VAULT_ID = constants_.vaultId;\n\n SUPPLY_TOKEN = constants_.supplyToken;\n BORROW_TOKEN = constants_.borrowToken;\n SUPPLY_DECIMALS = constants_.supplyDecimals;\n BORROW_DECIMALS = constants_.borrowDecimals;\n\n // @dev those slots are calculated in the deploymentLogics / VaultFactory\n LIQUIDITY_TOTAL_SUPPLY_SLOT = constants_.liquidityTotalSupplySlot;\n LIQUIDITY_TOTAL_BORROW_SLOT = constants_.liquidityTotalBorrowSlot;\n LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT = constants_.liquiditySupplyExchangePriceSlot;\n LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT = constants_.liquidityBorrowExchangePriceSlot;\n LIQUIDITY_USER_SUPPLY_SLOT = constants_.liquidityUserSupplySlot;\n LIQUIDITY_USER_BORROW_SLOT = constants_.liquidityUserBorrowSlot;\n\n ADMIN_IMPLEMENTATION = constants_.adminImplementation;\n SECONDARY_IMPLEMENTATION = constants_.secondaryImplementation;\n }\n}\n"
|
|
},
|
|
"contracts/protocols/vault/vaultT1/coreModule/events.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Events {\n /// @notice emitted when an operate() method is executed that changes collateral (`colAmt_`) / debt (debtAmt_`)\n /// amount for a `user_` position with `nftId_`. Receiver of any funds is the address `to_`.\n event LogOperate(address user_, uint256 nftId_, int256 colAmt_, int256 debtAmt_, address to_);\n\n /// @notice emitted when the exchange prices are updated in storage.\n event LogUpdateExchangePrice(uint256 supplyExPrice_, uint256 borrowExPrice_);\n\n /// @notice emitted when a liquidation has been executed.\n event LogLiquidate(address liquidator_, uint256 colAmt_, uint256 debtAmt_, address to_);\n\n /// @notice emitted when `absorb()` was executed to absorb bad debt.\n event LogAbsorb(uint colAbsorbedRaw_, uint debtAbsorbedRaw_);\n\n /// @notice emitted when a `rebalance()` has been executed, balancing out total supply / borrow between Vault\n /// and Fluid Liquidity pools.\n /// if `colAmt_` is positive then profit, meaning withdrawn from vault and sent to rebalancer address.\n /// if `colAmt_` is negative then loss, meaning transfer from rebalancer address to vault and deposit.\n /// if `debtAmt_` is positive then profit, meaning borrow from vault and sent to rebalancer address.\n /// if `debtAmt_` is negative then loss, meaning transfer from rebalancer address to vault and payback.\n event LogRebalance(int colAmt_, int debtAmt_);\n}\n"
|
|
},
|
|
"contracts/protocols/vault/vaultT1/coreModule/helpers.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { Variables } from \"../common/variables.sol\";\nimport { ConstantVariables } from \"./constantVariables.sol\";\nimport { Events } from \"./events.sol\";\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { BigMathVault } from \"../../../../libraries/bigMathVault.sol\";\nimport { LiquidityCalcs } from \"../../../../libraries/liquidityCalcs.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\nimport { Error } from \"../../error.sol\";\n\n/// @dev Fluid vault protocol helper methods. Mostly used for `operate()` and `liquidate()` methods of CoreModule.\nabstract contract Helpers is Variables, ConstantVariables, Events, Error {\n using BigMathMinified for uint256;\n using BigMathVault for uint256;\n\n /// @notice Calculates new vault exchange prices. Does not update values in storage.\n /// @param vaultVariables2_ exactly same as vaultVariables2 from storage\n /// @return liqSupplyExPrice_ latest liquidity's supply token supply exchange price\n /// @return liqBorrowExPrice_ latest liquidity's borrow token borrow exchange price\n /// @return vaultSupplyExPrice_ latest vault's supply token exchange price\n /// @return vaultBorrowExPrice_ latest vault's borrow token exchange price\n function updateExchangePrices(\n uint256 vaultVariables2_\n )\n public\n view\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n )\n {\n // Fetching last stored rates\n uint rates_ = rates;\n\n (liqSupplyExPrice_, ) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT)\n );\n (, liqBorrowExPrice_) = LiquidityCalcs.calcExchangePrices(\n LIQUIDITY.readFromStorage(LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT)\n );\n\n uint256 oldLiqSupplyExPrice_ = (rates_ & X64);\n uint256 oldLiqBorrowExPrice_ = ((rates_ >> 64) & X64);\n if (liqSupplyExPrice_ < oldLiqSupplyExPrice_ || liqBorrowExPrice_ < oldLiqBorrowExPrice_) {\n // new liquidity exchange price is < than the old one. liquidity exchange price should only ever increase.\n // If not, something went wrong and avoid proceeding with unknown outcome.\n revert FluidVaultError(ErrorTypes.VaultT1__LiquidityExchangePriceUnexpected);\n }\n\n // liquidity Exchange Prices always increases in next block. Hence substraction with old will never be negative\n // uint64 * 1e18 is the max the number that could be\n unchecked {\n // Calculating increase in supply exchange price w.r.t last stored liquidity's exchange price\n // vaultSupplyExPrice_ => supplyIncreaseInPercent_\n vaultSupplyExPrice_ = ((((liqSupplyExPrice_ * 1e18) / oldLiqSupplyExPrice_) - 1e18) *\n (vaultVariables2_ & X16)) / 10000; // supply rate magnifier\n\n // Calculating increase in borrow exchange price w.r.t last stored liquidity's exchange price\n // vaultBorrowExPrice_ => borrowIncreaseInPercent_\n vaultBorrowExPrice_ = ((((liqBorrowExPrice_ * 1e18) / oldLiqBorrowExPrice_) - 1e18) *\n ((vaultVariables2_ >> 16) & X16)) / 10000; // borrow rate magnifier\n\n // It's extremely hard the exchange prices to overflow even in 100 years but if it does it's not an\n // issue here as we are not updating on storage\n // (rates_ >> 128) & X64) -> last stored vault's supply token exchange price\n vaultSupplyExPrice_ = (((rates_ >> 128) & X64) * (1e18 + vaultSupplyExPrice_)) / 1e18;\n // (rates_ >> 192) -> last stored vault's borrow token exchange price (no need to mask with & X64 as it is anyway max 64 bits)\n vaultBorrowExPrice_ = ((rates_ >> 192) * (1e18 + vaultBorrowExPrice_)) / 1e18;\n }\n }\n\n /// note admin module is also calling this function self call\n /// @dev updating exchange price on storage. Only need to update on storage when changing supply or borrow magnifier\n function updateExchangePricesOnStorage()\n public\n returns (\n uint256 liqSupplyExPrice_,\n uint256 liqBorrowExPrice_,\n uint256 vaultSupplyExPrice_,\n uint256 vaultBorrowExPrice_\n )\n {\n (liqSupplyExPrice_, liqBorrowExPrice_, vaultSupplyExPrice_, vaultBorrowExPrice_) = updateExchangePrices(\n vaultVariables2\n );\n\n if (\n liqSupplyExPrice_ > X64 || liqBorrowExPrice_ > X64 || vaultSupplyExPrice_ > X64 || vaultBorrowExPrice_ > X64\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExchangePriceOverFlow);\n }\n\n // Updating in storage\n rates =\n liqSupplyExPrice_ |\n (liqBorrowExPrice_ << 64) |\n (vaultSupplyExPrice_ << 128) |\n (vaultBorrowExPrice_ << 192);\n\n emit LogUpdateExchangePrice(vaultSupplyExPrice_, vaultBorrowExPrice_);\n }\n\n /// @dev fetches new user's position after liquidation. The new liquidated position's debt is decreased by 0.01%\n /// to make sure that branch's liquidity never becomes 0 as if it would have gotten 0 then there will be multiple cases that we would need to tackle.\n /// @param positionTick_ position's tick when it was last updated through operate\n /// @param positionTickId_ position's tick Id. This stores the debt factor and branch to make the first connection\n /// @param positionRawDebt_ position's raw debt when it was last updated through operate\n /// @param tickData_ position's tick's tickData just for minor comparison to know if data is moved to tick Id or is still in tick data\n /// @return final tick position after all the liquidation\n /// @return final debt of position after all the liquidation\n /// @return positionRawCol_ final collateral of position after all the liquidation\n /// @return branchId_ final branch's ID where the position is at currently\n /// @return branchData_ final branch's data where the position is at currently\n function fetchLatestPosition(\n int256 positionTick_,\n uint256 positionTickId_,\n uint256 positionRawDebt_,\n uint256 tickData_\n )\n public\n view\n returns (\n int256, // positionTick_\n uint256, // positionRawDebt_\n uint256 positionRawCol_,\n uint256 branchId_,\n uint256 branchData_\n )\n {\n uint256 initialPositionRawDebt_ = positionRawDebt_;\n uint256 connectionFactor_;\n bool isFullyLiquidated_;\n\n // Checking if tick's total ID = user's tick ID\n if (((tickData_ >> 1) & X24) == positionTickId_) {\n // fetching from tick data itself\n isFullyLiquidated_ = ((tickData_ >> 25) & 1) == 1;\n branchId_ = (tickData_ >> 26) & X30;\n connectionFactor_ = (tickData_ >> 56) & X50;\n } else {\n {\n uint256 tickLiquidationData_;\n unchecked {\n // Fetching tick's liquidation data. One variable contains data of 3 IDs. Tick Id mapping is starting from 1.\n tickLiquidationData_ =\n tickId[positionTick_][(positionTickId_ + 2) / 3] >>\n (((positionTickId_ + 2) % 3) * 85);\n }\n\n isFullyLiquidated_ = (tickLiquidationData_ & 1) == 1;\n branchId_ = (tickLiquidationData_ >> 1) & X30;\n connectionFactor_ = (tickLiquidationData_ >> 31) & X50;\n }\n }\n\n // data of branch\n branchData_ = branchData[branchId_];\n\n if (isFullyLiquidated_) {\n positionTick_ = type(int).min;\n positionRawDebt_ = 0;\n } else {\n // Below information about connection debt factor\n // If branch is merged, Connection debt factor is used to multiply in order to get perfect liquidation of user\n // For example: Considering user was at the top.\n // In first branch, the user liquidated to debt factor 0.5 and then branch got merged (branching starting from 1)\n // In second branch, it got liquidated to 0.4 but when the above branch merged the debt factor on this branch was 0.6\n // Meaning on 1st branch, user got liquidated by 50% & on 2nd by 33.33%. So a total of 66.6%.\n // What we will set a connection factor will be 0.6/0.5 = 1.2\n // So now to get user's position, this is what we'll do:\n // finalDebt = (0.4 / (1 * 1.2)) * debtBeforeLiquidation\n // 0.4 is current active branch's minima debt factor\n // 1 is debt factor from where user started\n // 1.2 is connection factor which we found out through 0.6 / 0.5\n while ((branchData_ & 3) == 2) {\n // If true then the branch is merged\n\n // userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor aka adjustmentDebtFactor\n connectionFactor_ = connectionFactor_.mulBigNumber(((branchData_ >> 116) & X50));\n if (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR) break; // user ~100% liquidated\n // Note we don't need updated branch data in case of 100% liquidated so saving gas for fetching it\n\n // Fetching new branch data\n branchId_ = (branchData_ >> 166) & X30; // Link to base branch of current branch\n branchData_ = branchData[branchId_];\n }\n // When the while loop breaks meaning the branch now has minima Debt Factor or is a closed branch;\n\n if (((branchData_ & 3) == 3) || (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR)) {\n // Branch got closed (or user liquidated ~100%). Hence make the user's position 0\n // Rare cases to get into this situation\n // Branch can get close often but once closed it's tricky that some user might come iterating through there\n // If a user comes then that user will be very mini user like some cents probably\n positionTick_ = type(int).min;\n positionRawDebt_ = 0;\n } else {\n // If branch is not merged, the main branch it's connected to then it'll have minima debt factor\n\n // position debt = debt * base branch minimaDebtFactor / connectionFactor\n positionRawDebt_ = positionRawDebt_.mulDivNormal(\n (branchData_ >> 116) & X50, // minimaDebtFactor\n connectionFactor_\n );\n\n unchecked {\n // Reducing user's liquidity by 0.01% if user got liquidated.\n // As this will make sure that the branch always have some debt even if all liquidated user left\n // This saves a lot more logics & consideration on Operate function\n // if we don't do this then we have to add logics related to closing the branch and factor connections accordingly.\n if (positionRawDebt_ > (initialPositionRawDebt_ / 100)) {\n positionRawDebt_ = (positionRawDebt_ * 9999) / 10000;\n } else {\n // if user debt reduced by more than 99% in liquidation then making user as fully liquidated\n positionRawDebt_ = 0;\n }\n }\n\n {\n if (positionRawDebt_ > 0) {\n // positionTick_ -> read minima tick of branch\n unchecked {\n positionTick_ = branchData_ & 4 == 4\n ? int((branchData_ >> 3) & X19)\n : -int((branchData_ >> 3) & X19);\n }\n // Calculating user's collateral\n uint256 ratioAtTick_ = TickMath.getRatioAtTick(int24(positionTick_));\n uint256 ratioOneLess_;\n unchecked {\n ratioOneLess_ = (ratioAtTick_ * 10000) / 10015;\n }\n // formula below for better readability:\n // length = ratioAtTick_ - ratioOneLess_\n // ratio = ratioOneLess_ + (length * positionPartials_) / X30\n // positionRawCol_ = (positionRawDebt_ * (1 << 96)) / ratio_\n positionRawCol_ =\n (positionRawDebt_ * TickMath.ZERO_TICK_SCALED_RATIO) /\n (ratioOneLess_ + ((ratioAtTick_ - ratioOneLess_) * ((branchData_ >> 22) & X30)) / X30);\n } else {\n positionTick_ = type(int).min;\n }\n }\n }\n }\n return (positionTick_, positionRawDebt_, positionRawCol_, branchId_, branchData_);\n }\n\n /// @dev sets `tick_` as having debt or no debt in storage `tickHasDebt` depending on `addOrRemove_`\n /// @param tick_ tick to add or remove from tickHasDebt\n /// @param addOrRemove_ if true then add else remove\n function _updateTickHasDebt(int tick_, bool addOrRemove_) internal {\n // Positive mapID_ starts from 0 & above and negative starts below 0.\n // tick 0 to 255 will have mapId_ as 0 while tick -256 to -1 will have mapId_ as -1.\n unchecked {\n int mapId_ = tick_ < 0 ? ((tick_ + 1) / 256) - 1 : tick_ / 256;\n\n // in case of removing:\n // (tick == 255) tickHasDebt[mapId_] - 1 << 255\n // (tick == 0) tickHasDebt[mapId_] - 1 << 0\n // (tick == -1) tickHasDebt[mapId_] - 1 << 255\n // (tick == -256) tickHasDebt[mapId_] - 1 << 0\n // in case of adding:\n // (tick == 255) tickHasDebt[mapId_] - 1 << 255\n // (tick == 0) tickHasDebt[mapId_] - 1 << 0\n // (tick == -1) tickHasDebt[mapId_] - 1 << 255\n // (tick == -256) tickHasDebt[mapId_] - 1 << 0\n uint position_ = uint(tick_ - (mapId_ * 256));\n\n tickHasDebt[mapId_] = addOrRemove_\n ? tickHasDebt[mapId_] | (1 << position_)\n : tickHasDebt[mapId_] & ~(1 << position_);\n }\n }\n\n /// @dev gets next perfect top tick (tick which is not liquidated)\n /// @param topTick_ current top tick which will no longer be top tick\n /// @return nextTick_ next top tick which will become the new top tick\n function _fetchNextTopTick(int topTick_) internal view returns (int nextTick_) {\n int mapId_;\n uint tickHasDebt_;\n\n unchecked {\n mapId_ = topTick_ < 0 ? ((topTick_ + 1) / 256) - 1 : topTick_ / 256;\n uint bitsToRemove_ = uint(-topTick_ + (mapId_ * 256 + 256));\n // Removing current top tick from tickHasDebt\n tickHasDebt_ = (tickHasDebt[mapId_] << bitsToRemove_) >> bitsToRemove_;\n\n // For last user remaining in vault there could be a lot of iterations in the while loop.\n // Chances of this to happen is extremely low (like ~0%)\n while (true) {\n if (tickHasDebt_ > 0) {\n nextTick_ = mapId_ * 256 + int(tickHasDebt_.mostSignificantBit()) - 1;\n break;\n }\n\n // Reducing mapId_ by 1 in every loop; if it reaches to -129 then no filled tick exist, meaning it's the last tick\n if (--mapId_ == -129) {\n nextTick_ = type(int).min;\n break;\n }\n\n tickHasDebt_ = tickHasDebt[mapId_];\n }\n }\n }\n\n /// @dev adding debt to a particular tick\n /// @param totalColRaw_ total raw collateral of position\n /// @param netDebtRaw_ net raw debt (total debt - dust debt)\n /// @return tick_ tick where the debt is being added\n /// @return tickId_ tick current id\n /// @return userRawDebt_ user's total raw debt\n /// @return rawDust_ dust debt used for adjustment\n function _addDebtToTickWrite(\n uint256 totalColRaw_,\n uint256 netDebtRaw_ // debtRaw - dust\n ) internal returns (int256 tick_, uint256 tickId_, uint256 userRawDebt_, uint256 rawDust_) {\n if (netDebtRaw_ < 10000) {\n // thrown if user's debt is too low\n revert FluidVaultError(ErrorTypes.VaultT1__UserDebtTooLow);\n }\n // tick_ & ratio_ returned from library is round down. Hence increasing it by 1 and increasing ratio by 1 tick.\n uint ratio_ = (netDebtRaw_ * TickMath.ZERO_TICK_SCALED_RATIO) / totalColRaw_;\n (tick_, ratio_) = TickMath.getTickAtRatio(ratio_);\n unchecked {\n ++tick_;\n ratio_ = (ratio_ * 10015) / 10000;\n }\n userRawDebt_ = (ratio_ * totalColRaw_) >> 96;\n rawDust_ = userRawDebt_ - netDebtRaw_;\n\n // Current state of tick\n uint256 tickData_ = tickData[tick_];\n tickId_ = (tickData_ >> 1) & X24;\n\n uint tickNewDebt_;\n if (tickId_ > 0 && tickData_ & 1 == 0) {\n // Current debt in the tick\n uint256 tickExistingRawDebt_ = (tickData_ >> 25) & X64;\n tickExistingRawDebt_ = (tickExistingRawDebt_ >> 8) << (tickExistingRawDebt_ & X8);\n\n // Tick's already initialized and not liquidated. Hence simply add the debt\n tickNewDebt_ = tickExistingRawDebt_ + userRawDebt_;\n if (tickExistingRawDebt_ == 0) {\n // Adding tick into tickHasDebt\n _updateTickHasDebt(tick_, true);\n }\n } else {\n // Liquidation happened or tick getting initialized for the very first time.\n if (tickId_ > 0) {\n // Meaning a liquidation happened. Hence move the data to tickID\n unchecked {\n uint tickMap_ = (tickId_ + 2) / 3;\n // Adding 2 in ID so we can get right mapping ID. For example for ID 1, 2 & 3 mapping should be 1 and so on..\n // For example shift for id 1 should be 0, for id 2 should be 85, for id 3 it should be 170 and so on..\n tickId[tick_][tickMap_] =\n tickId[tick_][tickMap_] |\n ((tickData_ >> 25) << (((tickId_ + 2) % 3) * 85));\n }\n }\n // Increasing total ID by one\n unchecked {\n ++tickId_;\n }\n tickNewDebt_ = userRawDebt_;\n\n // Adding tick into tickHasDebt\n _updateTickHasDebt(tick_, true);\n }\n if (tickNewDebt_ < 10000) {\n // thrown if tick's debt/liquidity is too low\n revert FluidVaultError(ErrorTypes.VaultT1__TickDebtTooLow);\n }\n tickData[tick_] = (tickId_ << 1) | (tickNewDebt_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 25);\n }\n\n /// @dev sets new top tick. If it comes to this function then that means current top tick is perfect tick.\n /// if next top tick is liquidated then unitializes the current non liquidated branch and make the liquidated branch as current branch\n /// @param topTick_ current top tick\n /// @param vaultVariables_ vaultVariables of storage but with newer updates\n /// @return newVaultVariables_ newVaultVariables_ updated vault variable internally to this function\n /// @return newTopTick_ new top tick\n function _setNewTopTick(\n int topTick_,\n uint vaultVariables_\n ) internal returns (uint newVaultVariables_, int newTopTick_) {\n // This function considers that the current top tick was not liquidated\n // Overall flow of function:\n // if new top tick liquidated (aka base branch's minima tick) -> Close the current branch and make base branch as current branch\n // if new top tick not liquidated -> update things in current branch.\n // if new top tick is not liquidated and same tick exist in base branch then tick is considered as not liquidated.\n\n uint branchId_ = (vaultVariables_ >> 22) & X30; // branch id of current branch\n\n uint256 branchData_ = branchData[branchId_];\n int256 baseBranchMinimaTick_;\n if ((branchData_ >> 196) & 1 == 1) {\n baseBranchMinimaTick_ = int((branchData_ >> 197) & X19);\n } else {\n unchecked {\n baseBranchMinimaTick_ = -int((branchData_ >> 197) & X19);\n }\n if (baseBranchMinimaTick_ == 0) {\n // meaning the current branch is the master branch\n baseBranchMinimaTick_ = type(int).min;\n }\n }\n\n // Returns type(int).min if no top tick exist\n int nextTopTickNotLiquidated_ = _fetchNextTopTick(topTick_);\n\n newTopTick_ = baseBranchMinimaTick_ > nextTopTickNotLiquidated_\n ? baseBranchMinimaTick_\n : nextTopTickNotLiquidated_;\n\n if (newTopTick_ == type(int).min) {\n // if this happens that means this was the last user of the vault :(\n vaultVariables_ = vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001;\n } else if (newTopTick_ == nextTopTickNotLiquidated_) {\n // New top tick exist in current non liquidated branch\n if (newTopTick_ < 0) {\n unchecked {\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001) |\n (uint(-newTopTick_) << 3);\n }\n } else {\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001) |\n 4 | // setting top tick as positive\n (uint(newTopTick_) << 3);\n }\n } else {\n // if this happens that means base branch exists & is the next top tick\n // Remove current non liquidated branch as active.\n // Not deleting here as it's going to get initialize again whenever a new top tick comes\n branchData[branchId_] = 0;\n // Inserting liquidated branch's minima tick\n unchecked {\n vaultVariables_ =\n (vaultVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00000000000000000001) |\n 2 | // Setting top tick as liquidated\n (((branchData_ >> 196) & X20) << 2) | // new current top tick = base branch minima tick\n (((branchData_ >> 166) & X30) << 22) | // new current branch id = base branch id\n ((branchId_ - 1) << 52); // reduce total branch id by 1\n }\n }\n\n newVaultVariables_ = vaultVariables_;\n }\n\n constructor(ConstantViews memory constants_) ConstantVariables(constants_) {}\n}\n"
|
|
},
|
|
"contracts/protocols/vault/vaultT1/coreModule/main.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidOracle } from \"../../../../oracle/fluidOracle.sol\";\n\nimport { TickMath } from \"../../../../libraries/tickMath.sol\";\nimport { BigMathMinified } from \"../../../../libraries/bigMathMinified.sol\";\nimport { BigMathVault } from \"../../../../libraries/bigMathVault.sol\";\nimport { LiquidityCalcs } from \"../../../../libraries/liquidityCalcs.sol\";\nimport { SafeTransfer } from \"../../../../libraries/safeTransfer.sol\";\n\nimport { Helpers } from \"./helpers.sol\";\nimport { LiquiditySlotsLink } from \"../../../../libraries/liquiditySlotsLink.sol\";\n\nimport { ErrorTypes } from \"../../errorTypes.sol\";\n\n/// @notice Fluid \"VaultT1\" (Vault Type 1). Fluid vault protocol main contract.\n/// Fluid Vault protocol is a borrow / lending protocol, allowing users to create collateral / borrow positions.\n/// All funds are deposited into / borrowed from Fluid Liquidity layer.\n/// Positions are represented through NFTs minted by the VaultFactory.\n/// Deployed by \"VaultFactory\" and linked together with VaultT1 AdminModule `ADMIN_IMPLEMENTATION` and\n/// FluidVaultT1Secondary (main2.sol) `SECONDARY_IMPLEMENTATION`.\n/// AdminModule & FluidVaultT1Secondary methods are delegateCalled, if the msg.sender has the required authorization.\n/// This contract links to an Oracle, which is used to assess collateral / debt value. Oracles implement the\n/// \"FluidOracle\" base contract and return the price in 1e27 precision.\n/// @dev For view methods / accessing data, use the \"VaultResolver\" periphery contract.\ncontract FluidVaultT1 is Helpers {\n using BigMathMinified for uint256;\n using BigMathVault for uint256;\n\n /// @dev Single function which handles supply, withdraw, borrow & payback\n /// @param nftId_ NFT ID for interaction. If 0 then create new NFT/position.\n /// @param newCol_ new collateral. If positive then deposit, if negative then withdraw, if 0 then do nohing\n /// @param newDebt_ new debt. If positive then borrow, if negative then payback, if 0 then do nohing\n /// @param to_ address where withdraw or borrow should go. If address(0) then msg.sender\n /// @return nftId_ if 0 then this returns the newly created NFT Id else returns the same NFT ID\n /// @return newCol_ final supply amount. Mainly if max withdraw using type(int).min then this is useful to get perfect amount else remain same as newCol_\n /// @return newDebt_ final borrow amount. Mainly if max payback using type(int).min then this is useful to get perfect amount else remain same as newDebt_\n function operate(\n uint256 nftId_, // if 0 then new position\n int256 newCol_, // if negative then withdraw\n int256 newDebt_, // if negative then payback\n address to_ // address at which the borrow & withdraw amount should go to. If address(0) then it'll go to msg.sender\n )\n public\n payable\n returns (\n uint256, // nftId_\n int256, // final supply amount. if - then withdraw\n int256 // final borrow amount. if - then payback\n )\n {\n uint256 vaultVariables_ = vaultVariables;\n // re-entrancy check\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n if (\n (newCol_ == 0 && newDebt_ == 0) ||\n // withdrawal or deposit cannot be too small\n ((newCol_ != 0) && (newCol_ > -10000 && newCol_ < 10000)) ||\n // borrow or payback cannot be too small\n ((newDebt_ != 0) && (newDebt_ > -10000 && newDebt_ < 10000))\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOperateAmount);\n }\n\n // Check msg.value aligns with input amounts if supply or borrow token is native token.\n // Note that it's not possible for a vault to have both supply token and borrow token as native token.\n if (SUPPLY_TOKEN == NATIVE_TOKEN && newCol_ > 0) {\n if (uint(newCol_) != msg.value) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n } else if (msg.value > 0) {\n if (!(BORROW_TOKEN == NATIVE_TOKEN && newDebt_ < 0)) {\n // msg.value sent along for withdraw, borrow, or non-native token operations\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n }\n\n OperateMemoryVars memory o_;\n // Temporary variables used as helpers at many places\n uint256 temp_;\n uint256 temp2_;\n int256 temp3_;\n\n o_.vaultVariables2 = vaultVariables2;\n\n temp_ = (vaultVariables_ >> 2) & X20;\n unchecked {\n o_.topTick = (temp_ == 0) ? type(int).min : ((temp_ & 1) == 1)\n ? int((temp_ >> 1) & X19)\n : -int((temp_ >> 1) & X19);\n }\n\n {\n // Fetching user's position\n if (nftId_ == 0) {\n // creating new position.\n o_.tick = type(int).min;\n // minting new NFT vault for user.\n nftId_ = VAULT_FACTORY.mint(VAULT_ID, msg.sender);\n // Adding 1 in total positions. Total positions cannot exceed 32bits as NFT minting checks for that\n unchecked {\n vaultVariables_ = vaultVariables_ + (1 << 210);\n }\n } else {\n // Updating existing position\n\n // checking owner only in case of withdraw or borrow\n if ((newCol_ < 0 || newDebt_ > 0) && (VAULT_FACTORY.ownerOf(nftId_) != msg.sender)) {\n revert FluidVaultError(ErrorTypes.VaultT1__NotAnOwner);\n }\n\n // temp_ => user's position data\n temp_ = positionData[nftId_];\n\n if (temp_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__NftNotOfThisVault);\n }\n // temp2_ => user's supply amount\n temp2_ = (temp_ >> 45) & X64;\n // Converting big number into normal number\n o_.colRaw = (temp2_ >> 8) << (temp2_ & X8);\n // temp2_ => user's dust debt amount\n temp2_ = (temp_ >> 109) & X64;\n // Converting big number into normal number\n o_.dustDebtRaw = (temp2_ >> 8) << (temp2_ & X8);\n\n // 1 is supply & 0 is borrow\n if (temp_ & 1 == 1) {\n // only supply position (has no debt)\n o_.tick = type(int).min;\n } else {\n // borrow position (has collateral & debt)\n unchecked {\n o_.tick = temp_ & 2 == 2 ? int((temp_ >> 2) & X19) : -int((temp_ >> 2) & X19);\n }\n o_.tickId = (temp_ >> 21) & X24;\n }\n }\n }\n\n // Get latest updated Position's debt & supply (if position is with debt -> not new / supply position)\n if (o_.tick > type(int).min) {\n // if entering this if statement then temp_ here will always be user's position data\n // extracting collateral exponent\n temp_ = (temp_ >> 45) & X8;\n // if exponent is > 0 then rounding up the collateral just for calculating debt\n unchecked {\n temp_ = temp_ == 0 ? (o_.colRaw + 1) : o_.colRaw + (1 << temp_);\n }\n // fetch current debt\n o_.debtRaw = ((TickMath.getRatioAtTick(int24(o_.tick)) * temp_) >> 96) + 1;\n\n // Tick data from user's tick\n temp_ = tickData[o_.tick];\n\n // Checking if tick is liquidated (first bit 1) OR if the total IDs of tick is greater than user's tick ID\n if (((temp_ & 1) == 1) || (((temp_ >> 1) & X24) > o_.tickId)) {\n // User got liquidated\n (\n // returns the position of the user if the user got liquidated.\n o_.tick,\n o_.debtRaw,\n o_.colRaw,\n temp2_, // final branchId from liquidation where position exist right now\n o_.branchData\n ) = fetchLatestPosition(o_.tick, o_.tickId, o_.debtRaw, temp_);\n\n if (o_.debtRaw > o_.dustDebtRaw) {\n // temp_ => branch's Debt\n temp_ = (o_.branchData >> 52) & X64;\n temp_ = (temp_ >> 8) << (temp_ & X8);\n\n // o_.debtRaw should always be < branch's Debt (temp_).\n // Taking margin (0.01%) in fetchLatestPosition to make sure it's always less\n temp_ -= o_.debtRaw;\n if (temp_ < 100) {\n // explicitly making sure that branch debt/liquidity doesn't get super low.\n temp_ = 100;\n }\n // Inserting updated branch's debt\n branchData[temp2_] =\n (o_.branchData & 0xfffffffffffffffffffffffffffffffffff0000000000000000fffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52);\n\n unchecked {\n // Converted positionRawDebt_ in net position debt\n o_.debtRaw -= o_.dustDebtRaw;\n }\n } else {\n // Liquidated 100% or almost 100%\n // absorbing dust debt\n absorbedDustDebt = absorbedDustDebt + o_.dustDebtRaw - o_.debtRaw;\n o_.debtRaw = 0;\n o_.colRaw = 0;\n }\n } else {\n // User didn't got liquidated\n // Removing user's debt from tick data\n // temp2_ => debt in tick\n temp2_ = (temp_ >> 25) & X64;\n // below require can fail when a user liquidity is extremely low (talking about way less than even $1)\n // adding require meaning this vault user won't be able to interact unless someone makes the liquidity in tick as non 0.\n // reason of adding is the tick has already removed from everywhere. Can removing it again break something? Better to simply remove that case entirely\n if (temp2_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__TickIsEmpty);\n }\n // Converting big number into normal number\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n // debtInTick (temp2_) < debtToRemove (o_.debtRaw) that means minor precision error. Hence make the debtInTick as 0.\n // The precision error can be caused with Bigmath library limiting the precision to 2**56.\n unchecked {\n temp2_ = o_.debtRaw < temp2_ ? temp2_ - o_.debtRaw : 0;\n }\n\n if (temp2_ < 10000) {\n temp2_ = 0;\n // if debt becomes 0 then remove from tick has debt\n\n if (o_.tick == o_.topTick) {\n // if tick is top tick then current top tick is perfect tick -> fetch & set new top tick\n\n // Updating new top tick in vaultVariables_ and topTick_\n (vaultVariables_, o_.topTick) = _setNewTopTick(o_.topTick, vaultVariables_);\n }\n\n // Removing from tickHasDebt\n _updateTickHasDebt(o_.tick, false);\n }\n\n tickData[o_.tick] = (temp_ & X25) | (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 25);\n\n // Converted positionRawDebt_ in net position debt\n o_.debtRaw -= o_.dustDebtRaw;\n }\n o_.dustDebtRaw = 0;\n }\n\n // Setting the current tick into old tick as the position tick is going to change now.\n o_.oldTick = o_.tick;\n o_.oldColRaw = o_.colRaw;\n o_.oldNetDebtRaw = o_.debtRaw;\n\n {\n (o_.liquidityExPrice, , o_.supplyExPrice, o_.borrowExPrice) = updateExchangePrices(o_.vaultVariables2);\n\n {\n // supply or withdraw\n if (newCol_ > 0) {\n // supply new col, rounding down\n o_.colRaw += (uint256(newCol_) * EXCHANGE_PRICES_PRECISION) / o_.supplyExPrice;\n // final user's collateral should not be above 2**128 bits\n if (o_.colRaw > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n } else if (newCol_ < 0) {\n // if withdraw equals type(int).min then max withdraw\n if (newCol_ > type(int128).min) {\n // partial withdraw, rounding up removing extra wei from collateral\n temp3_ = ((newCol_ * int(EXCHANGE_PRICES_PRECISION)) / int256(o_.supplyExPrice)) - 1;\n unchecked {\n if (uint256(-temp3_) > o_.colRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessCollateralWithdrawal);\n }\n o_.colRaw -= uint256(-temp3_);\n }\n } else if (newCol_ == type(int).min) {\n // max withdraw, rounding up:\n // adding +1 to negative withdrawAmount newCol_ for safe rounding (reducing withdraw)\n newCol_ = -(int256((o_.colRaw * o_.supplyExPrice) / EXCHANGE_PRICES_PRECISION)) + 1;\n o_.colRaw = 0;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n }\n }\n {\n // borrow or payback\n if (newDebt_ > 0) {\n // borrow new debt, rounding up adding extra wei in debt\n temp_ = ((uint(newDebt_) * EXCHANGE_PRICES_PRECISION) / o_.borrowExPrice) + 1;\n // if borrow fee is 0 then it'll become temp_ + 0.\n // Only adding fee in o_.debtRaw and not in newDebt_ as newDebt_ is debt that needs to be borrowed from Liquidity\n // as we have added fee in debtRaw hence it will get added in user's position & vault's total borrow.\n // It can be collected with rebalance function.\n o_.debtRaw += temp_ + (temp_ * ((o_.vaultVariables2 >> 82) & X10)) / 10000;\n // final user's debt should not be above 2**128 bits\n if (o_.debtRaw > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n } else if (newDebt_ < 0) {\n // if payback equals type(int).min then max payback\n if (newDebt_ > type(int128).min) {\n // partial payback.\n // temp3_ => newDebt_ in raw terms, safe rounding up negative amount to rounding reduce payback\n temp3_ = (newDebt_ * int256(EXCHANGE_PRICES_PRECISION)) / int256(o_.borrowExPrice) + 1;\n unchecked {\n temp3_ = -temp3_;\n if (uint256(temp3_) > o_.debtRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessDebtPayback);\n }\n o_.debtRaw -= uint256(temp3_);\n }\n } else if (newDebt_ == type(int).min) {\n // max payback, rounding up amount that will be transferred in to pay back full debt:\n // subtracting -1 of negative debtAmount newDebt_ for safe rounding (increasing payback)\n newDebt_ = -(int256((o_.debtRaw * o_.borrowExPrice) / EXCHANGE_PRICES_PRECISION)) - 1;\n o_.debtRaw = 0;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__UserCollateralDebtExceed);\n }\n }\n }\n }\n\n // if position has no collateral or debt and user sends type(int).min for withdraw and payback then this results in 0\n // there's is no issue if it stays 0 but better to throw here to avoid checking for potential issues if there could be\n if (newCol_ == 0 && newDebt_ == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidOperateAmount);\n }\n\n // Assign new tick\n if (o_.debtRaw > 0) {\n // updating tickHasDebt in the below function if required\n // o_.debtRaw here is updated to new debt raw incl. dust debt (not net debt)\n unchecked {\n (o_.tick, o_.tickId, o_.debtRaw, o_.dustDebtRaw) = _addDebtToTickWrite(\n o_.colRaw,\n ((o_.debtRaw * 1000000001) / 1000000000) + 1\n );\n }\n\n if (newDebt_ < 0) {\n // anyone can payback debt of any position\n // hence, explicitly checking the debt should decrease\n if ((o_.debtRaw - o_.dustDebtRaw) > o_.oldNetDebtRaw) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidPaybackOrDeposit);\n }\n }\n if ((newCol_ > 0) && (newDebt_ == 0)) {\n // anyone can deposit collateral in any position\n // Hence, explicitly checking that new ratio should be less than old ratio\n if (\n (((o_.debtRaw - o_.dustDebtRaw) * TickMath.ZERO_TICK_SCALED_RATIO) / o_.colRaw) >\n ((o_.oldNetDebtRaw * TickMath.ZERO_TICK_SCALED_RATIO) / o_.oldColRaw)\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidPaybackOrDeposit);\n }\n }\n\n if (o_.tick >= o_.topTick) {\n // Updating topTick in storage\n // temp_ => tick to insert in vault variables\n unchecked {\n temp_ = o_.tick < 0 ? uint(-o_.tick) << 1 : (uint(o_.tick) << 1) | 1;\n }\n if (vaultVariables_ & 2 == 0) {\n // Current branch not liquidated. Hence, just update top tick\n vaultVariables_ =\n (vaultVariables_ & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000) |\n (temp_ << 2);\n } else {\n // Current branch liquidated\n // Initialize a new branch\n // temp2_ => totalBranchId_\n unchecked {\n temp2_ = ((vaultVariables_ >> 52) & X30) + 1; // would take 34 years to overflow if a new branch is created every second\n }\n // Connecting new active branch with current active branch which is now base branch\n // Current top tick is now base branch's minima tick\n branchData[temp2_] =\n (((vaultVariables_ >> 22) & X30) << 166) | // current branch id set as base branch id\n (((vaultVariables_ >> 2) & X20) << 196); // current top tick set as base branch minima tick\n // Updating new vault variables in memory with new branch\n vaultVariables_ =\n (vaultVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffc00000000000000000000) |\n (temp_ << 2) | // new top tick\n (temp2_ << 22) | // new branch id\n (temp2_ << 52); // total branch ids\n }\n }\n } else {\n // debtRaw_ remains 0 in this situation\n // This kind of position will not have any tick. Meaning it'll be a supply position.\n o_.tick = type(int).min;\n }\n\n {\n if (newCol_ < 0 || newDebt_ > 0) {\n // withdraw or borrow\n if (to_ == address(0)) {\n to_ = msg.sender;\n }\n\n // if debt is greater than 0 & transaction includes borrow or withdraw (incl. combinations such as deposit + borrow etc.)\n // -> check collateral factor\n if (o_.debtRaw > 0) {\n // Oracle returns price at 100% ratio.\n // converting oracle 160 bits into oracle address\n // temp_ => debt price w.r.t to col in 1e27\n temp_ = IFluidOracle(address(uint160(o_.vaultVariables2 >> 96))).getExchangeRate();\n // Note if price would come back as 0 `getTickAtRatio` will fail\n\n // Converting price in terms of raw amounts\n temp_ = (temp_ * o_.supplyExPrice) / o_.borrowExPrice;\n\n // temp2_ => ratio at CF. CF is in 3 decimals. 900 = 90%\n temp2_ = ((temp_ * ((o_.vaultVariables2 >> 32) & X10)) / 1000);\n\n // Price from oracle is in 1e27 decimals. Converting it into (1 << 96) decimals\n temp2_ = (temp2_ < 1e45)\n ? ((temp2_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27)\n : (temp2_ / 1e27) * TickMath.ZERO_TICK_SCALED_RATIO;\n\n // temp3_ => tickAtCF_\n (temp3_, ) = TickMath.getTickAtRatio(temp2_);\n if (o_.tick > temp3_) {\n unchecked {\n // calc for net debt can be unchecked as o_.dustDebtRaw can not be > o_.debtRaw:\n // o_.dustDebtRaw is the result of o_.debtRaw - x where x > 0 see _addDebtToTickWrite()\n if (\n o_.oldTick <= o_.tick ||\n (o_.debtRaw - o_.dustDebtRaw) > (((o_.oldNetDebtRaw * 1000000001) / 1000000000) + 1)\n ) {\n // Above CF, user should only be allowed to reduce ratio either by paying debt or by depositing more collateral\n // Not comparing collateral as user can potentially use safe/deleverage to reduce tick & debt.\n // On use of safe/deleverage, collateral will decrease but debt will decrease as well making the overall position safer.\n revert FluidVaultError(ErrorTypes.VaultT1__PositionAboveCF);\n }\n }\n }\n }\n }\n }\n\n {\n // Updating user's new position on storage\n // temp_ => tick to insert as user position tick\n if (o_.tick > type(int).min) {\n unchecked {\n temp_ = o_.tick < 0 ? (uint(-o_.tick) << 1) : ((uint(o_.tick) << 1) | 1);\n }\n } else {\n // if positionTick_ = type(int).min OR positionRawDebt_ == 0 then that means it's only supply position\n // (for case of positionRawDebt_ == 0, tick is set to type(int).min further up)\n temp_ = 0;\n }\n\n positionData[nftId_] =\n ((temp_ == 0) ? 1 : 0) | // setting if supply only position (1) or not (first bit)\n (temp_ << 1) |\n (o_.tickId << 21) |\n (o_.colRaw.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 45) |\n // dust debt is rounded down because user debt = debt - dustDebt. rounding up would mean we reduce user debt\n (o_.dustDebtRaw.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 109);\n }\n\n // Withdrawal gap to make sure there's always liquidity for liquidation\n // For example if withdrawal allowance is 15% on liquidity then we can limit operate's withdrawal allowance to 10%\n // this will allow liquidate function to get extra 5% buffer for potential liquidations.\n if (newCol_ < 0) {\n // extracting withdrawal gap which is in 0.1% precision.\n temp_ = (o_.vaultVariables2 >> 62) & X10;\n if (temp_ > 0) {\n // fetching user's supply slot data\n o_.userSupplyLiquidityData = LIQUIDITY.readFromStorage(LIQUIDITY_USER_SUPPLY_SLOT);\n\n // converting current user's supply from big number to normal\n temp2_ = (o_.userSupplyLiquidityData >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;\n temp2_ = (temp2_ >> 8) << (temp2_ & X8);\n\n // fetching liquidity's withdrawal limit\n temp3_ = int(LiquidityCalcs.calcWithdrawalLimitBeforeOperate(o_.userSupplyLiquidityData, temp2_));\n\n // max the number could go is vault's supply * 1000. Overflowing is almost impossible.\n unchecked {\n // (liquidityUserSupply - withdrawalGap - liquidityWithdrawaLimit) should be less than user's withdrawal\n if (\n (temp3_ > 0) &&\n (((int(temp2_ * (1000 - temp_)) / 1000)) - temp3_) <\n (((-newCol_) * int(EXCHANGE_PRICES_PRECISION)) / int(o_.liquidityExPrice))\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__WithdrawMoreThanOperateLimit);\n }\n }\n }\n }\n\n {\n // execute actions at Liquidity: deposit & payback is first and then withdraw & borrow\n if (newCol_ > 0) {\n // deposit\n LIQUIDITY.operate{ value: SUPPLY_TOKEN == NATIVE_TOKEN ? msg.value : 0 }(\n SUPPLY_TOKEN,\n newCol_,\n 0,\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n }\n if (newDebt_ < 0) {\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n unchecked {\n temp_ = uint(-newDebt_);\n if (msg.value > temp_) {\n SafeTransfer.safeTransferNative(msg.sender, msg.value - temp_);\n } else if (msg.value < temp_) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueOperate);\n }\n }\n } else {\n temp_ = 0;\n }\n // payback\n LIQUIDITY.operate{ value: temp_ }(\n BORROW_TOKEN,\n 0,\n newDebt_,\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n }\n if (newCol_ < 0) {\n // withdraw\n LIQUIDITY.operate(SUPPLY_TOKEN, newCol_, 0, to_, address(0), new bytes(0));\n }\n if (newDebt_ > 0) {\n // borrow\n LIQUIDITY.operate(BORROW_TOKEN, 0, newDebt_, address(0), to_, new bytes(0));\n }\n }\n\n {\n // Updating vault variables on storage\n\n // Calculating new total collateral & total debt.\n temp_ = (vaultVariables_ >> 82) & X64;\n temp_ = ((temp_ >> 8) << (temp_ & X8)) + o_.colRaw - o_.oldColRaw;\n temp2_ = (vaultVariables_ >> 146) & X64;\n temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) + (o_.debtRaw - o_.dustDebtRaw) - o_.oldNetDebtRaw;\n // Updating vault variables on storage. This will also reentrancy 0 back again\n // Converting total supply & total borrow in 64 bits (56 | 8) bignumber\n vaultVariables =\n (vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply\n (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow\n }\n\n emit LogOperate(msg.sender, nftId_, newCol_, newDebt_, to_);\n\n return (nftId_, newCol_, newDebt_);\n }\n\n /// @dev allows to liquidate all bad debt of all users at once. Liquidator can also liquidate partially any amount they want.\n /// @param debtAmt_ total debt to liquidate (aka debt token to swap into collateral token)\n /// @param colPerUnitDebt_ minimum collateral token per unit of debt in 1e18 decimals\n /// @param to_ address at which collateral token should go to.\n /// If dead address (0x000000000000000000000000000000000000dEaD) then reverts with custom error \"FluidLiquidateResult\"\n /// returning the actual collateral and actual debt liquidated. Useful to find max liquidatable amounts via try / catch.\n /// @param absorb_ if true then liquidate from absorbed first\n /// @return actualDebtAmt_ if liquidator sends debtAmt_ more than debt remaining to liquidate then actualDebtAmt_ changes from debtAmt_ else remains same\n /// @return actualColAmt_ total liquidated collateral which liquidator will get\n function liquidate(\n uint256 debtAmt_,\n uint256 colPerUnitDebt_, // min collateral needed per unit of debt in 1e18\n address to_,\n bool absorb_\n ) public payable returns (uint actualDebtAmt_, uint actualColAmt_) {\n LiquidateMemoryVars memory memoryVars_;\n\n uint vaultVariables_ = vaultVariables;\n\n // ############# turning re-entrancy bit on #############\n if (vaultVariables_ & 1 == 0) {\n // Updating on storage\n vaultVariables = vaultVariables_ | 1;\n } else {\n revert FluidVaultError(ErrorTypes.VaultT1__AlreadyEntered);\n }\n\n if (debtAmt_ < 10000 || debtAmt_ > X128) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidationAmt);\n }\n\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n if (msg.value != debtAmt_) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueLiquidate);\n }\n } else if (msg.value > 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidMsgValueLiquidate);\n }\n\n memoryVars_.vaultVariables2 = vaultVariables2;\n\n if (((vaultVariables_ >> 2) & X20) == 0) {\n revert FluidVaultError(ErrorTypes.VaultT1__TopTickDoesNotExist);\n }\n\n // Below are exchange prices of vaults\n (, , memoryVars_.supplyExPrice, memoryVars_.borrowExPrice) = updateExchangePrices(memoryVars_.vaultVariables2);\n\n CurrentLiquidity memory currentData_;\n BranchData memory branch_;\n // Temporary holder variables, used many times for different small things\n uint temp_;\n uint temp2_;\n\n {\n // ############# Setting current branch in memory #############\n\n // Updating branch related data\n branch_.id = (vaultVariables_ >> 22) & X30;\n branch_.data = branchData[branch_.id];\n branch_.debtFactor = (branch_.data >> 116) & X50;\n if (branch_.debtFactor == 0) {\n // Initializing branch debt factor. 35 | 15 bit number. Where full 35 bits and 15th bit is occupied.\n // Making the total number as (2**35 - 1) << 2**14.\n // note: initial debt factor can be any number.\n branch_.debtFactor = ((X35 << 15) | (1 << 14));\n }\n // fetching base branch's minima tick. if 0 that means it's a master branch\n temp_ = (branch_.data >> 196) & X20;\n if (temp_ > 0) {\n unchecked {\n branch_.minimaTick = (temp_ & 1) == 1 ? int256((temp_ >> 1) & X19) : -int256((temp_ >> 1) & X19);\n }\n } else {\n branch_.minimaTick = type(int).min;\n }\n }\n\n // extracting top tick as top tick will be the current tick\n unchecked {\n currentData_.tick = (vaultVariables_ & 4) == 4\n ? int256((vaultVariables_ >> 3) & X19)\n : -int256((vaultVariables_ >> 3) & X19);\n }\n // setting up status if top tick is liquidated or not\n currentData_.tickStatus = vaultVariables_ & 2 == 0 ? 1 : 2;\n // Tick info is mainly used as a place holder to store temporary tick related data\n // (it can be current or ref using same memory variable)\n TickData memory tickInfo_;\n tickInfo_.tick = currentData_.tick;\n\n {\n // ############# Oracle related stuff #############\n // Col price w.r.t debt. For example: 1 ETH = 1000 DAI\n // temp_ -> debtPerCol\n temp_ = IFluidOracle(address(uint160(memoryVars_.vaultVariables2 >> 96))).getExchangeRate(); // Price in 27 decimals\n // temp_ -> debtPerCol Converting in terms of raw amount\n temp_ = (temp_ * memoryVars_.supplyExPrice) / memoryVars_.borrowExPrice;\n // temp2_ -> Raw colPerDebt_ in 27 decimals\n temp2_ = 1e54 / temp_;\n\n // Liquidation penalty in 4 decimals (1e2 = 1%) (max: 10.23%) -> (vaultVariables2_ >> 72) & X10\n currentData_.colPerDebt = (temp2_ * (10000 + ((memoryVars_.vaultVariables2 >> 72) & X10))) / 10000;\n\n // get liquidiation tick (tick at liquidation threshold ratio)\n // Liquidation threshold in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 42) & X10\n // Dividing by 1e27 to convert temp_ into normal number\n temp_ = (temp_ < 1e45)\n ? ((temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27)\n : ((temp_ / 1e27) * TickMath.ZERO_TICK_SCALED_RATIO);\n // temp2_ -> liquidationRatio_\n temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 42) & X10)) / 1000;\n (memoryVars_.liquidationTick, ) = TickMath.getTickAtRatio(temp2_);\n\n // get liquidiation max limit tick (tick at liquidation max limit ratio)\n // Max limit in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 52) & X10\n // temp2_ -> maxRatio_\n temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 52) & X10)) / 1000;\n (memoryVars_.maxTick, ) = TickMath.getTickAtRatio(temp2_);\n }\n\n currentData_.debtRemaining = (debtAmt_ * EXCHANGE_PRICES_PRECISION) / memoryVars_.borrowExPrice;\n\n if (absorb_) {\n temp_ = absorbedLiquidity;\n // temp2_ -> absorbed col\n temp2_ = (temp_ >> 128) & X128;\n // temp_ -> absorbed debt\n temp_ = temp_ & X128;\n\n if (temp_ > currentData_.debtRemaining) {\n // Removing collateral in equal proportion as debt\n currentData_.totalColLiq = ((temp2_ * currentData_.debtRemaining) / temp_);\n temp2_ -= currentData_.totalColLiq;\n // Removing debt\n currentData_.totalDebtLiq = currentData_.debtRemaining;\n unchecked {\n temp_ -= currentData_.debtRemaining;\n }\n currentData_.debtRemaining = 0;\n\n // updating on storage\n absorbedLiquidity = temp_ | (temp2_ << 128);\n } else {\n // updating on storage\n absorbedLiquidity = 0;\n unchecked {\n currentData_.debtRemaining -= temp_;\n }\n currentData_.totalDebtLiq = temp_;\n currentData_.totalColLiq = temp2_;\n }\n }\n\n if (\n currentData_.tick > memoryVars_.liquidationTick && // current tick > liquidation tick\n currentData_.tick <= memoryVars_.maxTick // current tick <= max tick\n ) {\n if (currentData_.debtRemaining > 0) {\n // Stores liquidated debt & collateral in each loop\n uint debtLiquidated_;\n uint colLiquidated_;\n uint debtFactor_ = BigMathVault.TWO_POWER_64;\n\n TickHasDebt memory tickHasDebt_;\n unchecked {\n tickHasDebt_.mapId = (currentData_.tick < 0)\n ? (((currentData_.tick + 1) / 256) - 1)\n : (currentData_.tick / 256);\n }\n\n tickInfo_.ratio = TickMath.getRatioAtTick(tickInfo_.tick);\n\n if (currentData_.tickStatus == 1) {\n // top tick is not liquidated. Hence it's a perfect tick.\n currentData_.ratio = tickInfo_.ratio;\n // if current tick in liquidation is a perfect tick then it is also the next tick that has debt.\n tickHasDebt_.nextTick = currentData_.tick;\n } else {\n // top tick is liquidated. Hence it has partials.\n // next tick that has debt liquidity will have to be fetched from tickHasDebt\n unchecked {\n tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n tickInfo_.partials = (branch_.data >> 22) & X30;\n currentData_.ratio = tickInfo_.ratioOneLess + ((tickInfo_.length * tickInfo_.partials) / X30);\n }\n }\n\n while (true) {\n if (currentData_.tickStatus == 1) {\n // not liquidated -> Getting the debt from tick data itself\n temp2_ = tickData[currentData_.tick];\n // temp_ => tick debt\n temp_ = (temp2_ >> 25) & X64;\n // Converting big number into normal number\n temp_ = (temp_ >> 8) << (temp_ & X8);\n // Updating tickData on storage with removing debt & adding connection to branch\n tickData[currentData_.tick] =\n 1 | // set tick as liquidated\n (temp2_ & 0x1fffffe) | // set same total tick ids\n (branch_.id << 26) | // branch id where this tick got liquidated\n (branch_.debtFactor << 56);\n } else {\n // already liquidated -> Get the debt from branch data in big number\n // temp_ => tick debt\n temp_ = (branch_.data >> 52) & X64;\n // Converting big number into normal number\n temp_ = (temp_ >> 8) << (temp_ & X8);\n // Branch is getting updated over the end\n }\n\n // Adding new debt into active debt for liquidation\n currentData_.debt += temp_;\n\n // Adding new col into active col for liquidation\n // Ratio is in 2**96 decimals hence multiplying debt with 2**96 to get proper collateral\n currentData_.col += (temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / currentData_.ratio;\n\n if (\n (tickHasDebt_.nextTick == currentData_.tick && currentData_.tickStatus == 1) ||\n tickHasDebt_.tickHasDebt == 0\n ) {\n // Fetching next perfect tick with liquidity\n // tickHasDebt_.tickHasDebt == 0 will only happen in the first while loop\n // in the very first perfect tick liquidation it'll be 0\n if (tickHasDebt_.tickHasDebt == 0) {\n tickHasDebt_.tickHasDebt = tickHasDebt[tickHasDebt_.mapId];\n }\n\n // in 1st loop tickStatus can be 2. Meaning not a perfect current tick\n if (currentData_.tickStatus == 1) {\n unchecked {\n tickHasDebt_.bitsToRemove = uint(-currentData_.tick + (tickHasDebt_.mapId * 256 + 256));\n }\n // Removing current top tick from tickHasDebt\n tickHasDebt_.tickHasDebt =\n (tickHasDebt_.tickHasDebt << tickHasDebt_.bitsToRemove) >>\n tickHasDebt_.bitsToRemove;\n // Updating in storage if tickHasDebt becomes 0.\n if (tickHasDebt_.tickHasDebt == 0) {\n tickHasDebt[tickHasDebt_.mapId] = 0;\n }\n }\n\n // For last user remaining in vault there could be a lot of while loop.\n // Chances of this to happen is extremely low (like ~0%)\n while (true) {\n if (tickHasDebt_.tickHasDebt > 0) {\n unchecked {\n tickHasDebt_.nextTick =\n tickHasDebt_.mapId *\n 256 +\n int(tickHasDebt_.tickHasDebt.mostSignificantBit()) -\n 1;\n }\n break;\n }\n\n // tickHasDebt_.tickHasDebt == 0. Checking if minimum tick of this mapID is less than liquidationTick_\n // if true that means now the next tick is not needed as liquidation gets over minimum at liquidationTick_\n unchecked {\n if ((tickHasDebt_.mapId * 256) < memoryVars_.liquidationTick) {\n tickHasDebt_.nextTick = type(int).min;\n break;\n }\n\n // Fetching next tick has debt by decreasing tickHasDebt_.mapId first\n tickHasDebt_.tickHasDebt = tickHasDebt[--tickHasDebt_.mapId];\n }\n }\n }\n\n // Fetching refTick. refTick is the biggest tick of these 3:\n // 1. Next tick with liquidity (from tickHasDebt)\n // 2. Minima tick of current branch\n // 3. Liquidation threshold tick\n {\n // Setting currentData_.refTick & currentData_.refTickStatus\n if (\n branch_.minimaTick > tickHasDebt_.nextTick &&\n branch_.minimaTick > memoryVars_.liquidationTick\n ) {\n // next tick will be of base branch (merge)\n currentData_.refTick = branch_.minimaTick;\n currentData_.refTickStatus = 2;\n } else if (tickHasDebt_.nextTick > memoryVars_.liquidationTick) {\n // next tick will be next tick from perfect tick\n currentData_.refTick = tickHasDebt_.nextTick;\n currentData_.refTickStatus = 1;\n } else {\n // next tick is threshold tick\n currentData_.refTick = memoryVars_.liquidationTick;\n currentData_.refTickStatus = 3; // leads to end of liquidation loop\n }\n }\n\n // using tickInfo variable again for ref tick as we don't have the need for it any more\n tickInfo_.ratio = TickMath.getRatioAtTick(int24(currentData_.refTick));\n if (currentData_.refTickStatus == 2) {\n // merge current branch with base branch\n unchecked {\n tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n // Fetching base branch data to get the base branch's partial\n branch_.baseBranchData = branchData[((branch_.data >> 166) & X30)];\n tickInfo_.partials = (branch_.baseBranchData >> 22) & X30;\n tickInfo_.currentRatio =\n tickInfo_.ratioOneLess +\n ((tickInfo_.length * tickInfo_.partials) / X30);\n currentData_.refRatio = tickInfo_.currentRatio;\n }\n } else {\n // refTickStatus can only be 1 (next tick from perfect tick) or 3 (liquidation threshold tick)\n tickInfo_.currentRatio = tickInfo_.ratio;\n currentData_.refRatio = tickInfo_.ratio;\n tickInfo_.partials = X30;\n }\n\n // Formula: (debt_ - x) / (col_ - (x * colPerDebt_)) = ratioEnd_\n // x = ((ratioEnd_ * col) - debt_) / ((colPerDebt_ * ratioEnd_) - 1)\n // x is debtToLiquidate_\n // col_ = debt_ / ratioStart_ -> (currentData_.debt / currentData_.ratio)\n // ratioEnd_ is currentData_.refRatio\n //\n // Calculation results of numerator & denominator is always negative\n // which will cancel out to give positive output in the end so we can safely cast to uint.\n // for nominator:\n // ratioStart can only be >= ratioEnd so first part can only be reducing currentData_.debt leading to\n // currentData_.debt reduced - currentData_.debt original * 1e27 -> can only be a negative number\n // for denominator:\n // currentData_.colPerDebt and currentData_.refRatio are inversely proportional to each other.\n // the maximum value they can ever be is ~9.97e26 which is the 0.3% away from 100% because liquidation\n // threshold + liquidation penalty can never be > 99.7%. This can also be verified by going back from\n // min / max ratio values further up where we fetch oracle price etc.\n // as optimization we can inverse nominator and denominator subtraction to directly get a positive number.\n\n debtLiquidated_ =\n // nominator\n ((currentData_.debt - (currentData_.refRatio * currentData_.debt) / currentData_.ratio) *\n 1e27) /\n // denominator\n (1e27 - ((currentData_.colPerDebt * currentData_.refRatio) / TickMath.ZERO_TICK_SCALED_RATIO));\n\n colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27;\n\n if (currentData_.debt == debtLiquidated_) {\n debtLiquidated_ -= 1;\n }\n\n if (debtLiquidated_ >= currentData_.debtRemaining || currentData_.refTickStatus == 3) {\n // End of liquidation as full amount to liquidate or liquidation threshold tick has been reached;\n\n // Updating tickHasDebt on storage.\n tickHasDebt[tickHasDebt_.mapId] = tickHasDebt_.tickHasDebt;\n\n if (debtLiquidated_ >= currentData_.debtRemaining) {\n // Liquidation ended between currentTick & refTick.\n // Not all of liquidatable debt is actually liquidated -> recalculate\n debtLiquidated_ = currentData_.debtRemaining;\n colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27;\n // Liquidating to debt. temp_ => final ratio after liquidation\n // liquidatable debt - debtLiquidated / liquidatable col - colLiquidated\n temp_ =\n ((currentData_.debt - debtLiquidated_) * TickMath.ZERO_TICK_SCALED_RATIO) /\n (currentData_.col - colLiquidated_);\n // Fetching tick of where liquidation ended\n (tickInfo_.tick, tickInfo_.ratioOneLess) = TickMath.getTickAtRatio(temp_);\n // Increasing tick by 1 as final ratio will probably be a partial\n unchecked {\n ++tickInfo_.tick;\n tickInfo_.ratio = (tickInfo_.ratioOneLess * 10015) / 10000;\n tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess;\n tickInfo_.partials = ((temp_ - tickInfo_.ratioOneLess) * X30) / tickInfo_.length;\n\n // Taking edge cases where partial comes as 0 or X30 meaning perfect tick.\n // Hence, increasing or reducing it by 1 as liquidation tick cannot be perfect tick.\n tickInfo_.partials = tickInfo_.partials == 0 ? 1 : tickInfo_.partials >= X30\n ? X30 - 1\n : tickInfo_.partials;\n }\n } else {\n // End in liquidation threshold.\n // finalRatio_ = currentData_.refRatio;\n // Increasing liquidation threshold tick by 1 partial. With 1 partial it'll reach to the next tick.\n // Ratio change will be negligible. Doing this as liquidation threshold tick can also be a perfect non-liquidated tick.\n unchecked {\n tickInfo_.tick = currentData_.refTick + 1;\n }\n // Making partial as 1 so it doesn't stay perfect tick\n tickInfo_.partials = 1;\n // length is not needed as only partials are written to storage\n }\n\n // debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt\n // -> debtFactor * leftOverDebt / liquidatableDebt\n debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt;\n currentData_.totalDebtLiq += debtLiquidated_;\n currentData_.debt -= debtLiquidated_; // currentData_.debt => leftOverDebt after debtLiquidated_\n currentData_.totalColLiq += colLiquidated_;\n currentData_.col -= colLiquidated_; // currentData_.col => leftOverCol after colLiquidated_\n\n // Updating branch's debt factor & write to storage as liquidation is over\n branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_);\n\n if (currentData_.debt < 100) {\n // this can happen when someone tries to create a dust tick\n revert FluidVaultError(ErrorTypes.VaultT1__BranchDebtTooLow);\n }\n\n unchecked {\n // Tick to insert\n temp2_ = tickInfo_.tick < 0\n ? (uint(-tickInfo_.tick) << 1)\n : ((uint(tickInfo_.tick) << 1) | 1);\n }\n\n // Updating Branch data with debt factor, debt, partials, minima tick & assigning is liquidated\n branchData[branch_.id] =\n ((branch_.data >> 166) << 166) |\n 1 | // set as liquidated\n (temp2_ << 2) | // minima tick of branch\n (tickInfo_.partials << 22) |\n (currentData_.debt.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52) | // branch debt\n (branch_.debtFactor << 116);\n\n // Updating vault variables with current branch & tick\n vaultVariables_ =\n ((vaultVariables_ >> 52) << 52) |\n 2 | // set as liquidated\n (temp2_ << 2) | // top tick\n (branch_.id << 22);\n break;\n }\n\n unchecked {\n // debtLiquidated_ >= currentData_.debtRemaining leads to loop break in if statement above\n // so this can be unchecked\n currentData_.debtRemaining -= debtLiquidated_;\n }\n\n // debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt\n // -> debtFactor * leftOverDebt / liquidatableDebt\n debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt;\n currentData_.totalDebtLiq += debtLiquidated_;\n currentData_.debt -= debtLiquidated_;\n currentData_.totalColLiq += colLiquidated_;\n currentData_.col -= colLiquidated_;\n\n // updating branch's debt factor\n branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_);\n // Setting debt factor as 1 << 64 again\n debtFactor_ = BigMathVault.TWO_POWER_64;\n\n if (currentData_.refTickStatus == 2) {\n // ref tick is base branch's minima hence merging current branch to base branch\n // and making base branch as current branch.\n\n // read base branch related data\n temp_ = (branch_.data >> 166) & X30; // temp_ -> base branch id\n temp2_ = branch_.baseBranchData;\n {\n uint newBranchDebtFactor_ = (temp2_ >> 116) & X50;\n\n // connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor\n uint connectionFactor_ = newBranchDebtFactor_.divBigNumber(branch_.debtFactor);\n // Updating current branch in storage\n branchData[branch_.id] =\n ((branch_.data >> 166) << 166) | // deleting debt / partials / minima tick\n 2 | // setting as merged\n (connectionFactor_ << 116); // set new connectionFactor\n\n // Storing base branch in memory\n // Updating branch ID to base branch ID\n branch_.id = temp_;\n // Updating branch data with base branch data\n branch_.data = temp2_;\n // Remove next branch connection from base branch\n branch_.debtFactor = newBranchDebtFactor_;\n // temp_ => minima tick of base branch\n temp_ = (temp2_ >> 196) & X20;\n if (temp_ > 0) {\n unchecked {\n branch_.minimaTick = (temp_ & 1) == 1\n ? int256((temp_ >> 1) & X19)\n : -int256((temp_ >> 1) & X19);\n }\n } else {\n branch_.minimaTick = type(int).min;\n }\n }\n }\n\n // Making refTick as currentTick\n currentData_.tick = currentData_.refTick;\n currentData_.tickStatus = currentData_.refTickStatus;\n currentData_.ratio = currentData_.refRatio;\n }\n }\n }\n\n // calculating net token amounts using exchange price\n actualDebtAmt_ = (currentData_.totalDebtLiq * memoryVars_.borrowExPrice) / EXCHANGE_PRICES_PRECISION;\n actualColAmt_ = (currentData_.totalColLiq * memoryVars_.supplyExPrice) / EXCHANGE_PRICES_PRECISION;\n\n // Chances of this to happen are in few wei\n if (actualDebtAmt_ > debtAmt_) {\n // calc new actualColAmt_ via ratio.\n actualColAmt_ = actualColAmt_ * (debtAmt_ / actualDebtAmt_);\n actualDebtAmt_ = debtAmt_;\n }\n\n if (((actualColAmt_ * 1e18) / actualDebtAmt_) < colPerUnitDebt_) {\n revert FluidVaultError(ErrorTypes.VaultT1__ExcessSlippageLiquidation);\n }\n\n if (to_ == 0x000000000000000000000000000000000000dEaD) {\n // revert with liquidated amounts if to_ address is the dead address.\n // this can be used in a resolver to find the max liquidatable amounts.\n revert FluidLiquidateResult(actualColAmt_, actualDebtAmt_);\n }\n\n // payback at Liquidity\n if (BORROW_TOKEN == NATIVE_TOKEN) {\n temp_ = actualDebtAmt_;\n if (actualDebtAmt_ < msg.value) {\n unchecked {\n // subtraction can be unchecked because of if check above\n SafeTransfer.safeTransferNative(msg.sender, msg.value - actualDebtAmt_);\n }\n }\n // else if actualDebtAmt_ > msg.value not possible as actualDebtAmt_ can maximally be debtAmt_ and\n // msg.value == debtAmt_ is checked in the beginning of function.\n } else {\n temp_ = 0;\n }\n unchecked {\n // payback at liquidity\n LIQUIDITY.operate{ value: temp_ }(\n BORROW_TOKEN,\n 0,\n -int(actualDebtAmt_),\n address(0),\n address(0),\n abi.encode(msg.sender)\n );\n // withdraw at liquidity\n LIQUIDITY.operate(SUPPLY_TOKEN, -int(actualColAmt_), 0, to_, address(0), new bytes(0));\n }\n\n // Calculating new total collateral & total debt.\n // temp_ -> total supply\n temp_ = (vaultVariables_ >> 82) & X64;\n temp_ = ((temp_ >> 8) << (temp_ & X8)) - currentData_.totalColLiq;\n // temp2_ -> total borrow\n temp2_ = (vaultVariables_ >> 146) & X64;\n temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) - currentData_.totalDebtLiq;\n // Updating vault variables on storage\n // Converting total supply & total borrow in 64 bits (56 | 8) bignumber\n vaultVariables =\n (vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) |\n (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply\n (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow\n\n emit LogLiquidate(msg.sender, actualColAmt_, actualDebtAmt_, to_);\n }\n\n /// @dev absorb function absorbs the bad debt if the bad debt is above max limit. The main use of it is\n /// if the bad debt didn't got liquidated in time maybe due to sudden price drop or bad debt was extremely small to liquidate\n /// and the bad debt goes above 100% ratio then there's no incentive for anyone to liquidate now\n /// hence absorb functions absorbs that bad debt to allow newer bad debt to liquidate seamlessly\n /// if absorbing were to happen after this it's on governance on how to deal with it\n /// although it can still be removed through liquidate via liquidator if the price goes back up and liquidation becomes beneficial\n /// upon absorbed user position gets 100% liquidated.\n function absorb() public {\n _spell(SECONDARY_IMPLEMENTATION, msg.data);\n }\n\n /// @dev Checks total supply of vault's in Liquidity Layer & Vault contract and rebalance it accordingly\n /// if vault supply is more than Liquidity Layer then deposit difference through reserve/rebalance contract\n /// if vault supply is less than Liquidity Layer then withdraw difference to reserve/rebalance contract\n /// if vault borrow is more than Liquidity Layer then borrow difference to reserve/rebalance contract\n /// if vault borrow is less than Liquidity Layer then payback difference through reserve/rebalance contract\n function rebalance() external payable returns (int supplyAmt_, int borrowAmt_) {\n (supplyAmt_, borrowAmt_) = abi.decode(_spell(SECONDARY_IMPLEMENTATION, msg.data), (int, int));\n }\n\n /// @dev liquidity callback for cheaper token transfers in case of deposit or payback.\n /// only callable by Liquidity during an operation.\n function liquidityCallback(address token_, uint amount_, bytes calldata data_) external {\n if (msg.sender != address(LIQUIDITY))\n revert FluidVaultError(ErrorTypes.VaultT1__InvalidLiquidityCallbackAddress);\n if (vaultVariables & 1 == 0) revert FluidVaultError(ErrorTypes.VaultT1__NotEntered);\n\n SafeTransfer.safeTransferFrom(token_, abi.decode(data_, (address)), address(LIQUIDITY), amount_);\n }\n\n constructor(ConstantViews memory constants_) Helpers(constants_) {\n // Note that vaults are deployed by VaultFactory so we somewhat trust the values being passed in\n\n // Setting branch in vault.\n vaultVariables = (vaultVariables) | (1 << 22) | (1 << 52);\n\n uint liqSupplyExchangePrice_ = (LIQUIDITY.readFromStorage(LIQUIDITY_SUPPLY_EXCHANGE_PRICE_SLOT) >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64;\n uint liqBorrowExchangePrice_ = (LIQUIDITY.readFromStorage(LIQUIDITY_BORROW_EXCHANGE_PRICE_SLOT) >>\n LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64;\n\n if (\n liqSupplyExchangePrice_ < EXCHANGE_PRICES_PRECISION || liqBorrowExchangePrice_ < EXCHANGE_PRICES_PRECISION\n ) {\n revert FluidVaultError(ErrorTypes.VaultT1__TokenNotInitialized);\n }\n // Updating initial rates in storage\n rates =\n liqSupplyExchangePrice_ |\n (liqBorrowExchangePrice_ << 64) |\n (EXCHANGE_PRICES_PRECISION << 128) |\n (EXCHANGE_PRICES_PRECISION << 192);\n }\n\n fallback() external {\n if (!(VAULT_FACTORY.isGlobalAuth(msg.sender) || VAULT_FACTORY.isVaultAuth(msg.sender, address(this)))) {\n revert FluidVaultError(ErrorTypes.VaultT1__NotAnAuth);\n }\n\n // Delegate the current call to `implementation`.\n // This does not return to its internall call site, it will return directly to the external caller.\n // solhint-disable-next-line no-inline-assembly\n _spell(ADMIN_IMPLEMENTATION, msg.data);\n }\n\n function _spell(address target_, bytes memory data_) private returns (bytes memory response_) {\n assembly {\n let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)\n let size := returndatasize()\n\n response_ := mload(0x40)\n mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))\n mstore(response_, size)\n returndatacopy(add(response_, 0x20), 0, size)\n\n switch iszero(succeeded)\n case 1 {\n // throw if delegatecall failed\n returndatacopy(0x00, 0x00, size)\n revert(0x00, size)\n }\n }\n }\n}\n"
|
|
},
|
|
"contracts/protocols/vault/vaultT1/coreModule/structs.sol": {
|
|
"content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Structs {\n // structs are used to mitigate Stack too deep errors\n\n struct OperateMemoryVars {\n // ## User's position before update ##\n uint oldColRaw;\n uint oldNetDebtRaw; // total debt - dust debt\n int oldTick;\n // ## User's position after update ##\n uint colRaw;\n uint debtRaw; // total debt - dust debt\n uint dustDebtRaw;\n int tick;\n uint tickId;\n // others\n uint256 vaultVariables2;\n uint256 branchId;\n int256 topTick;\n uint liquidityExPrice;\n uint supplyExPrice;\n uint borrowExPrice;\n uint branchData;\n // user's supply slot data in liquidity\n uint userSupplyLiquidityData;\n }\n\n struct BranchData {\n uint id;\n uint data;\n uint ratio;\n uint debtFactor;\n int minimaTick;\n uint baseBranchData;\n }\n\n struct TickData {\n int tick;\n uint data;\n uint ratio;\n uint ratioOneLess;\n uint length;\n uint currentRatio; // current tick is ratio with partials.\n uint partials;\n }\n\n // note: All the below token amounts are in raw form.\n struct CurrentLiquidity {\n uint256 debtRemaining; // Debt remaining to liquidate\n uint256 debt; // Current liquidatable debt before reaching next check point\n uint256 col; // Calculate using debt & ratioCurrent\n uint256 colPerDebt; // How much collateral to liquidate per unit of Debt\n uint256 totalDebtLiq; // Total debt liquidated till now\n uint256 totalColLiq; // Total collateral liquidated till now\n int tick; // Current tick to liquidate\n uint ratio; // Current ratio to liquidate\n uint tickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick\n int refTick; // ref tick to liquidate\n uint refRatio; // ratio at ref tick\n uint refTickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick, if 3 that means it's a liquidation threshold\n }\n\n struct TickHasDebt {\n int tick; // current tick\n int nextTick; // next tick with liquidity\n int mapId; // mapping ID of tickHasDebt\n uint bitsToRemove; // liquidity to remove till tick_ so we can search for next tick\n uint tickHasDebt; // getting tickHasDebt_ from tickHasDebt[mapId_]\n uint mostSigBit; // most significant bit in tickHasDebt_ to get the next tick\n }\n\n struct LiquidateMemoryVars {\n uint256 vaultVariables2;\n int liquidationTick;\n int maxTick;\n uint256 supplyExPrice;\n uint256 borrowExPrice;\n }\n\n struct AbsorbMemoryVariables {\n uint256 supplyExPrice;\n uint256 borrowExPrice;\n uint256 debtAbsorbed;\n uint256 colAbsorbed;\n uint256 vaultVariables2;\n int256 startingTick;\n uint256 mostSigBit;\n }\n\n struct ConstantViews {\n address liquidity;\n address factory;\n address adminImplementation;\n address secondaryImplementation;\n address supplyToken;\n address borrowToken;\n uint8 supplyDecimals;\n uint8 borrowDecimals;\n uint vaultId;\n bytes32 liquidityTotalSupplySlot;\n bytes32 liquidityTotalBorrowSlot;\n bytes32 liquiditySupplyExchangePriceSlot;\n bytes32 liquidityBorrowExchangePriceSlot;\n bytes32 liquidityUserSupplySlot;\n bytes32 liquidityUserBorrowSlot;\n }\n\n struct RebalanceMemoryVariables {\n uint256 liqSupplyExPrice;\n uint256 liqBorrowExPrice;\n uint256 vaultSupplyExPrice;\n uint256 vaultBorrowExPrice;\n }\n}\n"
|
|
},
|
|
"solmate/src/utils/SSTORE2.sol": {
|
|
"content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Read and write to persistent storage at a fraction of the cost.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)\n/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)\nlibrary SSTORE2 {\n uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.\n\n /*//////////////////////////////////////////////////////////////\n WRITE LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function write(bytes memory data) internal returns (address pointer) {\n // Prefix the bytecode with a STOP opcode to ensure it cannot be called.\n bytes memory runtimeCode = abi.encodePacked(hex\"00\", data);\n\n bytes memory creationCode = abi.encodePacked(\n //---------------------------------------------------------------------------------------------------------------//\n // Opcode | Opcode + Arguments | Description | Stack View //\n //---------------------------------------------------------------------------------------------------------------//\n // 0x60 | 0x600B | PUSH1 11 | codeOffset //\n // 0x59 | 0x59 | MSIZE | 0 codeOffset //\n // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset //\n // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset //\n // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset //\n // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset //\n // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //\n // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //\n // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) //\n // 0xf3 | 0xf3 | RETURN | //\n //---------------------------------------------------------------------------------------------------------------//\n hex\"60_0B_59_81_38_03_80_92_59_39_F3\", // Returns all code in the contract except for the first 11 (0B in hex) bytes.\n runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.\n );\n\n /// @solidity memory-safe-assembly\n assembly {\n // Deploy a new contract with the generated creation code.\n // We start 32 bytes into the code to avoid copying the byte length.\n pointer := create(0, add(creationCode, 32), mload(creationCode))\n }\n\n require(pointer != address(0), \"DEPLOYMENT_FAILED\");\n }\n\n /*//////////////////////////////////////////////////////////////\n READ LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function read(address pointer) internal view returns (bytes memory) {\n return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);\n }\n\n function read(address pointer, uint256 start) internal view returns (bytes memory) {\n start += DATA_OFFSET;\n\n return readBytecode(pointer, start, pointer.code.length - start);\n }\n\n function read(\n address pointer,\n uint256 start,\n uint256 end\n ) internal view returns (bytes memory) {\n start += DATA_OFFSET;\n end += DATA_OFFSET;\n\n require(pointer.code.length >= end, \"OUT_OF_BOUNDS\");\n\n return readBytecode(pointer, start, end - start);\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL HELPER LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function readBytecode(\n address pointer,\n uint256 start,\n uint256 size\n ) private view returns (bytes memory data) {\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n data := mload(0x40)\n\n // Update the free memory pointer to prevent overriding our data.\n // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).\n // Adding 31 to size and running the result through the logic above ensures\n // the memory pointer remains word-aligned, following the Solidity convention.\n mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))\n\n // Store the size of the data in the first 32 byte chunk of free memory.\n mstore(data, size)\n\n // Copy the code into memory right after the 32 bytes we used to store the size.\n extcodecopy(pointer, add(data, 32), start, size)\n }\n }\n}\n"
|
|
}
|
|
},
|
|
"settings": {
|
|
"optimizer": {
|
|
"enabled": true,
|
|
"runs": 10000000
|
|
},
|
|
"evmVersion": "paris",
|
|
"outputSelection": {
|
|
"*": {
|
|
"*": [
|
|
"abi",
|
|
"evm.bytecode",
|
|
"evm.deployedBytecode",
|
|
"evm.methodIdentifiers",
|
|
"metadata",
|
|
"devdoc",
|
|
"userdoc",
|
|
"storageLayout",
|
|
"evm.gasEstimates"
|
|
],
|
|
"": [
|
|
"ast"
|
|
]
|
|
}
|
|
},
|
|
"metadata": {
|
|
"useLiteralContent": true
|
|
}
|
|
}
|
|
} |