Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
329316152 | 3 hrs ago | 0.05914679 ETH | ||||
329316152 | 3 hrs ago | 0.05914679 ETH | ||||
329316152 | 3 hrs ago | 7.1116817 ETH | ||||
329316152 | 3 hrs ago | 7.1116817 ETH | ||||
329314632 | 4 hrs ago | 0.01582063 ETH | ||||
329314632 | 4 hrs ago | 0.01582063 ETH | ||||
329314632 | 4 hrs ago | 9.84059151 ETH | ||||
329314632 | 4 hrs ago | 9.84059151 ETH | ||||
329280869 | 6 hrs ago | 0.01 ETH | ||||
329247146 | 8 hrs ago | 0.00137252 ETH | ||||
329247146 | 8 hrs ago | 0.00137252 ETH | ||||
329247146 | 8 hrs ago | 3.43243656 ETH | ||||
329247146 | 8 hrs ago | 3.43243656 ETH | ||||
329246587 | 8 hrs ago | 0.00071167 ETH | ||||
329246587 | 8 hrs ago | 0.00071167 ETH | ||||
329246587 | 8 hrs ago | 3.55931215 ETH | ||||
329246587 | 8 hrs ago | 3.55931215 ETH | ||||
329246084 | 8 hrs ago | 3.57660474 ETH | ||||
329246084 | 8 hrs ago | 3.57660474 ETH | ||||
329245606 | 8 hrs ago | 3.55672629 ETH | ||||
329245606 | 8 hrs ago | 3.55672629 ETH | ||||
329245177 | 8 hrs ago | 3.5650624 ETH | ||||
329245177 | 8 hrs ago | 3.5650624 ETH | ||||
329245063 | 8 hrs ago | 3.56568505 ETH | ||||
329245063 | 8 hrs ago | 3.56568505 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
FluidVaultT4
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 10000000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { ErrorTypes } from "../../errorTypes.sol"; import { FluidVault } from "../../vaultTypesCommon/coreModule/main.sol"; /// @notice Fluid "VaultT4" (Vault Type 4). Fluid vault protocol main contract. T4 -> Smart collateral | Smart debt /// Fluid Vault protocol is a borrow / lending protocol, allowing users to create collateral / borrow positions. /// All funds are deposited into / borrowed from Fluid Liquidity layer. /// Positions are represented through NFTs minted by the VaultFactory. /// Deployed by "VaultFactory" and linked together with Vault AdminModule `ADMIN_IMPLEMENTATION` and /// FluidVaultSecondary (main2.sol) `SECONDARY_IMPLEMENTATION`. /// AdminModule & FluidVaultSecondary methods are delegateCalled, if the msg.sender has the required authorization. /// This contract links to an Oracle, which is used to assess collateral / debt value. Oracles implement the /// "FluidOracle" base contract and return the price in 1e27 precision. /// @dev For view methods / accessing data, use the "VaultResolver" periphery contract. // // vaults can only be deployed for tokens that are listed at Liquidity (constructor reverts otherwise // if either the exchange price for the supply token or the borrow token is still not set at Liquidity). abstract contract Internals is FluidVault { function _debtLiquidateBefore( uint token0DebtAmt_, uint token1DebtAmt_, uint debtSharesMin_ ) internal returns (uint shares_) { // paying back and burning shares if (token0DebtAmt_ == 0 && token1DebtAmt_ == 0) { // when burning shares, debt amount should always be > 0 (aka payback) revert FluidVaultError(ErrorTypes.VaultDex__InvalidOperateAmount); } shares_ = BORROW.payback{ value: (BORROW_TOKEN0 == NATIVE_TOKEN) ? token0DebtAmt_ : (BORROW_TOKEN1 == NATIVE_TOKEN) ? token1DebtAmt_ : 0 }(token0DebtAmt_, token1DebtAmt_, debtSharesMin_, false); } function _colLiquidatePerfectAfter( uint perfectColShares_, uint token0ColAmtPerUnitShares_, uint token1ColAmtPerUnitShares_, address to_ ) internal returns (uint newColToken0_, uint newColToken1_) { uint colToken0Min_ = (token0ColAmtPerUnitShares_ * perfectColShares_) / 1e18; uint colToken1Min_ = (token1ColAmtPerUnitShares_ * perfectColShares_) / 1e18; if (colToken0Min_ > 0 && colToken1Min_ > 0) { (newColToken0_, newColToken1_) = SUPPLY.withdrawPerfect( perfectColShares_, colToken0Min_, colToken1Min_, to_ ); } else if (colToken0Min_ > 0 && colToken1Min_ == 0) { // withdraw only in token0, newColToken1_ remains 0 (newColToken0_) = SUPPLY.withdrawPerfectInOneToken(perfectColShares_, colToken0Min_, colToken1Min_, to_); } else if (colToken0Min_ == 0 && colToken1Min_ > 0) { // withdraw only in token1, newColToken0_ remains 0 (newColToken1_) = SUPPLY.withdrawPerfectInOneToken(perfectColShares_, colToken0Min_, colToken1Min_, to_); } else { // both sent as 0 revert FluidVaultError(ErrorTypes.VaultDex__InvalidOperateAmount); } } function _debtLiquidatePerfectPayback( uint perfectDebtShares_, uint token0DebtAmtPerUnitShares_, uint token1DebtAmtPerUnitShares_ ) internal returns (uint token0DebtPaid_, uint token1DebtPaid_) { uint debtToken0Min_ = (token0DebtAmtPerUnitShares_ * perfectDebtShares_) / 1e18; uint debtToken1Min_ = (token1DebtAmtPerUnitShares_ * perfectDebtShares_) / 1e18; if (debtToken0Min_ > 0 && debtToken1Min_ > 0) { (token0DebtPaid_, token1DebtPaid_) = BORROW.paybackPerfect{ value: (BORROW_TOKEN0 == NATIVE_TOKEN) ? debtToken0Min_ : (BORROW_TOKEN1 == NATIVE_TOKEN) ? debtToken1Min_ : 0 }(perfectDebtShares_, debtToken0Min_, debtToken1Min_, false); } else if (debtToken0Min_ > 0 && debtToken1Min_ == 0) { // payback only in token0, token1DebtPaid_ remains 0 (token0DebtPaid_) = BORROW.paybackPerfectInOneToken{ value: (BORROW_TOKEN0 == NATIVE_TOKEN) ? debtToken0Min_ : 0 }(perfectDebtShares_, debtToken0Min_, debtToken1Min_, false); } else if (debtToken0Min_ == 0 && debtToken1Min_ > 0) { // payback only in token1, token0DebtPaid_ remains 0 (token1DebtPaid_) = BORROW.paybackPerfectInOneToken{ value: (BORROW_TOKEN1 == NATIVE_TOKEN) ? debtToken1Min_ : 0 }(perfectDebtShares_, debtToken0Min_, debtToken1Min_, false); } else { // both sent as 0 revert FluidVaultError(ErrorTypes.VaultDex__InvalidOperateAmount); } } constructor(ConstantViews memory constants_) FluidVault(constants_) {} } contract FluidVaultT4 is Internals { /// @notice Performs operations on a vault position /// @dev This function allows users to modify their vault position by adjusting collateral and debt /// @param nftId_ The ID of the NFT representing the vault position /// @param newColToken0_ The change in collateral amount for token0 (positive for deposit, negative for withdrawal) /// @param newColToken1_ The change in collateral amount for token1 (positive for deposit, negative for withdrawal) /// @param colSharesMinMax_ Min or max collateral shares to mint or burn (positive for deposit, negative for withdrawal) /// @param newDebtToken0_ The change in debt amount for token0 (positive for borrowing, negative for repayment) /// @param newDebtToken1_ The change in debt amount for token1 (positive for borrowing, negative for repayment) /// @param debtSharesMinMax_ Min or max debt shares to burn or mint (positive for borrowing, negative for repayment) /// @param to_ The address to receive funds (if address(0), defaults to msg.sender) /// @return supplyAmt_ Final supply amount (negative if withdrawal occurred) /// @return borrowAmt_ Final borrow amount (negative if repayment occurred) function operate( uint nftId_, int newColToken0_, int newColToken1_, int colSharesMinMax_, int newDebtToken0_, int newDebtToken1_, int debtSharesMinMax_, address to_ ) external payable _dexFromAddress returns ( uint256, // nftId_ int256, // final supply amount. if - then withdraw int256 // final borrow amount. if - then payback ) { return abi.decode(_spell(OPERATE_IMPLEMENTATION, msg.data), (uint, int, int)); } /// @notice Performs operations on a vault position with perfect collateral shares /// @dev This function allows users to modify their vault position by adjusting collateral and debt /// @param nftId_ The ID of the NFT representing the vault position /// @param perfectColShares_ The change in collateral shares (positive for deposit, negative for withdrawal) /// @param colToken0MinMax_ Min or max collateral amount of token0 to withdraw or deposit (positive for deposit, negative for withdrawal) /// @param colToken1MinMax_ Min or max collateral amount of token1 to withdraw or deposit (positive for deposit, negative for withdrawal) /// @param perfectDebtShares_ The change in debt shares (positive for borrowing, negative for repayment) /// @param debtToken0MinMax_ Min or max debt amount for token0 to borrow or payback (positive for borrowing, negative for repayment) /// @param debtToken1MinMax_ Min or max debt amount for token1 to borrow or payback (positive for borrowing, negative for repayment) /// @param to_ The address to receive funds (if address(0), defaults to msg.sender) /// @return nftId_ The ID of the NFT representing the updated vault position /// @return r_ int256 array of return values: /// 0 - final col shares amount (can only change on max withdrawal) /// 1 - token0 deposit or withdraw amount /// 2 - token1 deposit or withdraw amount /// 3 - final debt shares amount (can only change on max payback) /// 4 - token0 borrow or payback amount /// 5 - token1 borrow or payback amount function operatePerfect( uint nftId_, int perfectColShares_, int colToken0MinMax_, int colToken1MinMax_, int perfectDebtShares_, int debtToken0MinMax_, int debtToken1MinMax_, address to_ ) external payable _dexFromAddress returns ( uint256, // nftId_ int256[] memory r_ ) { return abi.decode(_spell(OPERATE_IMPLEMENTATION, msg.data), (uint, int256[])); } /// @notice Liquidates a vault position /// @dev This function allows users to liquidate a vault position by adjusting collateral and debt /// @param token0DebtAmt_ The amount of debt in token0 to payback /// @param token1DebtAmt_ The amount of debt in token1 to payback /// @param debtSharesMin_ The minimum number of debt shares to liquidate /// @param colPerUnitDebt_ The collateral amount per unit of debt shares /// @param token0ColAmtPerUnitShares_ The collateral amount per unit of debt shares for token0 (in 1e18) /// @param token1ColAmtPerUnitShares_ The collateral amount per unit of debt shares for token1 (in 1e18) /// @param to_ The address to receive withdrawn collateral (if address(0), defaults to msg.sender) /// @param absorb_ Whether to liquidate absorbed liquidity as well /// @return actualDebtShares_ The actual number of debt shares liquidated /// @return actualColShares_ The actual number of collateral shares liquidated /// @return token0Col_ The amount of token0 collateral withdrawn /// @return token1Col_ The amount of token1 collateral withdrawn function liquidate( uint256 token0DebtAmt_, uint256 token1DebtAmt_, uint256 debtSharesMin_, uint256 colPerUnitDebt_, // col per unit is w.r.t debt shares and not token0/1 debt amount uint256 token0ColAmtPerUnitShares_, // in 1e18 uint256 token1ColAmtPerUnitShares_, // in 1e18 address to_, bool absorb_ ) external payable _dexFromAddress returns (uint256 actualDebtShares_, uint256 actualColShares_, uint256 token0Col_, uint256 token1Col_) { uint vaultVariables_ = vaultVariables; // ############# turning re-entrancy bit on ############# if (vaultVariables_ & 1 == 0) { // Updating on storage vaultVariables = vaultVariables_ | 1; } else { revert FluidVaultError(ErrorTypes.Vault__AlreadyEntered); } uint initialEth_ = address(this).balance - msg.value; to_ = to_ == address(0) ? msg.sender : to_; uint sharesPaid_ = _debtLiquidateBefore(token0DebtAmt_, token1DebtAmt_, debtSharesMin_); (actualDebtShares_, actualColShares_, vaultVariables_) = abi.decode( _liquidate(sharesPaid_, colPerUnitDebt_, to_, absorb_, vaultVariables_), (uint, uint, uint) ); if (actualDebtShares_ < sharesPaid_) { // shares paid should never be more than actual liquidation available revert FluidVaultError(ErrorTypes.VaultDex__DebtSharesPaidMoreThanAvailableLiquidation); } (token0Col_, token1Col_) = _colLiquidatePerfectAfter( actualColShares_, token0ColAmtPerUnitShares_, token1ColAmtPerUnitShares_, to_ ); // disabling re-entrancy and updating on storage vaultVariables = vaultVariables_; _validateEth(initialEth_); } struct LiquidatePerfect { uint256 vaultVariables; uint256 initialEth; } /// @notice Liquidates a vault position with perfect collateral shares /// @dev This function allows users to liquidate a vault position by adjusting collateral and debt /// @param debtShares_ The number of debt shares to liquidate, if 0 then we are only absorbing the debt /// @param token0DebtAmtPerUnitShares_ The debt amount per unit of debt shares for token0 (in 1e18) /// @param token1DebtAmtPerUnitShares_ The debt amount per unit of debt shares for token1 (in 1e18) /// @param colPerUnitDebt_ The collateral amount per unit of debt shares (in 1e18) /// @param token0ColAmtPerUnitShares_ The collateral amount per unit of debt shares for token0 (in 1e18) /// @param token1ColAmtPerUnitShares_ The collateral amount per unit of debt shares for token1 (in 1e18) /// @param to_ The address to receive withdrawn collateral or borrowed tokens (if address(0), defaults to msg.sender) /// @param absorb_ Whether to liquidate absorbed liquidity as well /// @return actualDebtShares_ The actual number of debt shares liquidated /// @return token0Debt_ The amount of debt in token0 that was paid back /// @return token1Debt_ The amount of debt in token1 that was paid back /// @return actualColShares_ The actual number of collateral shares liquidated /// @return token0Col_ The amount of collateral in token0 that was withdrawn /// @return token1Col_ The amount of collateral in token1 that was withdrawn function liquidatePerfect( uint256 debtShares_, uint256 token0DebtAmtPerUnitShares_, uint256 token1DebtAmtPerUnitShares_, uint256 colPerUnitDebt_, // col per unit is w.r.t debt shares and not token0/1 debt amount uint256 token0ColAmtPerUnitShares_, // in 1e18 uint256 token1ColAmtPerUnitShares_, // in 1e18 address to_, bool absorb_ ) external payable _dexFromAddress returns ( uint256 actualDebtShares_, uint256 token0Debt_, uint256 token1Debt_, uint256 actualColShares_, uint256 token0Col_, uint256 token1Col_ ) { LiquidatePerfect memory lp_; lp_.vaultVariables = vaultVariables; // ############# turning re-entrancy bit on ############# if (lp_.vaultVariables & 1 == 0) { // Updating on storage vaultVariables = lp_.vaultVariables | 1; } else { revert FluidVaultError(ErrorTypes.Vault__AlreadyEntered); } lp_.initialEth = address(this).balance - msg.value; to_ = to_ == address(0) ? msg.sender : to_; (actualDebtShares_, actualColShares_, lp_.vaultVariables) = abi.decode( _liquidate(debtShares_, colPerUnitDebt_, to_, absorb_, lp_.vaultVariables), (uint, uint, uint) ); // if debtShares_ is 0, then we are only absorbing the debt if (debtShares_ > 0) { (token0Debt_, token1Debt_) = _debtLiquidatePerfectPayback( actualDebtShares_, token0DebtAmtPerUnitShares_, token1DebtAmtPerUnitShares_ ); (token0Col_, token1Col_) = _colLiquidatePerfectAfter( actualColShares_, token0ColAmtPerUnitShares_, token1ColAmtPerUnitShares_, to_ ); } // disabling re-entrancy and updating on storage vaultVariables = lp_.vaultVariables; _validateEth(lp_.initialEth); } constructor(ConstantViews memory constants_) Internals(constants_) { // Note that vaults are deployed by VaultFactory so we somewhat trust the values being passed in } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol) pragma solidity ^0.8.0; import "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Enumerable is IERC721 { /** * @dev Returns the total amount of tokens stored by the contract. */ function totalSupply() external view returns (uint256); /** * @dev Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. */ function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); /** * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. */ function tokenByIndex(uint256 index) external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IProxy { function setAdmin(address newAdmin_) external; function setDummyImplementation(address newDummyImplementation_) external; function addImplementation(address implementation_, bytes4[] calldata sigs_) external; function removeImplementation(address implementation_) external; function getAdmin() external view returns (address); function getDummyImplementation() external view returns (address); function getImplementationSigs(address impl_) external view returns (bytes4[] memory); function getSigsImplementation(bytes4 sig_) external view returns (address); function readFromStorage(bytes32 slot_) external view returns (uint256 result_); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; /// @notice implements calculation of address for contracts deployed through CREATE. /// Accepts contract deployed from which address & nonce library AddressCalcs { /// @notice Computes the address of a contract based /// @param deployedFrom_ Address from which the contract was deployed /// @param nonce_ Nonce at which the contract was deployed /// @return contract_ Address of deployed contract function addressCalc(address deployedFrom_, uint nonce_) internal pure returns (address contract_) { // @dev based on https://ethereum.stackexchange.com/a/61413 // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment // hence, nonce of vault deployment starts with 1. bytes memory data; if (nonce_ == 0x00) { return address(0); } else if (nonce_ <= 0x7f) { data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployedFrom_, uint8(nonce_)); } else if (nonce_ <= 0xff) { data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployedFrom_, bytes1(0x81), uint8(nonce_)); } else if (nonce_ <= 0xffff) { data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployedFrom_, bytes1(0x82), uint16(nonce_)); } else if (nonce_ <= 0xffffff) { data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployedFrom_, bytes1(0x83), uint24(nonce_)); } else { data = abi.encodePacked(bytes1(0xda), bytes1(0x94), deployedFrom_, bytes1(0x84), uint32(nonce_)); } return address(uint160(uint256(keccak256(data)))); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; /// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits. /// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision /// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can /// result in significant gas cost reduction due to storage space reduction. /// 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. /// @dev roundUp is more like a increase 1, which happens everytime for the same number. /// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number. library BigMathMinified { /// @dev constants to use for `roundUp` input param to increase readability bool internal constant ROUND_DOWN = false; bool internal constant ROUND_UP = true; /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision). /// e.g.: /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits] /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary) /// => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000 /// ^-------------------- 51(exponent) -------------- ^ /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563) /// exponent = 0011,0011 (51) /// bigNumber = 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 (572493200179) /// /// @param normal number which needs to be converted into Big Number /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision)) /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent)) /// @param roundUp signals if result should be rounded down or up /// @return bigNumber converted bigNumber (coefficient << exponent) function toBigNumber( uint256 normal, uint256 coefficientSize, uint256 exponentSize, bool roundUp ) internal pure returns (uint256 bigNumber) { assembly { let lastBit_ let number_ := normal if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) { number_ := shr(0x80, number_) lastBit_ := 0x80 } if gt(number_, 0xFFFFFFFFFFFFFFFF) { number_ := shr(0x40, number_) lastBit_ := add(lastBit_, 0x40) } if gt(number_, 0xFFFFFFFF) { number_ := shr(0x20, number_) lastBit_ := add(lastBit_, 0x20) } if gt(number_, 0xFFFF) { number_ := shr(0x10, number_) lastBit_ := add(lastBit_, 0x10) } if gt(number_, 0xFF) { number_ := shr(0x8, number_) lastBit_ := add(lastBit_, 0x8) } if gt(number_, 0xF) { number_ := shr(0x4, number_) lastBit_ := add(lastBit_, 0x4) } if gt(number_, 0x3) { number_ := shr(0x2, number_) lastBit_ := add(lastBit_, 0x2) } if gt(number_, 0x1) { lastBit_ := add(lastBit_, 1) } if gt(number_, 0) { lastBit_ := add(lastBit_, 1) } if lt(lastBit_, coefficientSize) { // for throw exception lastBit_ := coefficientSize } let exponent := sub(lastBit_, coefficientSize) let coefficient := shr(exponent, normal) if and(roundUp, gt(exponent, 0)) { // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number coefficient := add(coefficient, 1) if eq(shl(coefficientSize, 1), coefficient) { // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1. coefficient := shl(sub(coefficientSize, 1), 1) exponent := add(exponent, 1) } } if iszero(lt(exponent, shl(exponentSize, 1))) { // if exponent is >= exponentSize, the normal number is too big to fit within // BigNumber with too small sizes for coefficient and exponent revert(0, 0) } bigNumber := shl(exponentSize, coefficient) bigNumber := add(bigNumber, exponent) } } /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask` function fromBigNumber( uint256 bigNumber, uint256 exponentSize, uint256 exponentMask ) internal pure returns (uint256 normal) { assembly { let coefficient := shr(exponentSize, bigNumber) let exponent := and(bigNumber, exponentMask) normal := shl(exponent, coefficient) } } /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format). /// e.g. /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 /// lastBit = ^--------------------------------- 83 ----------------------------------------^ function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) { assembly { let number_ := normal if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) { number_ := shr(0x80, number_) lastBit := 0x80 } if gt(number_, 0xFFFFFFFFFFFFFFFF) { number_ := shr(0x40, number_) lastBit := add(lastBit, 0x40) } if gt(number_, 0xFFFFFFFF) { number_ := shr(0x20, number_) lastBit := add(lastBit, 0x20) } if gt(number_, 0xFFFF) { number_ := shr(0x10, number_) lastBit := add(lastBit, 0x10) } if gt(number_, 0xFF) { number_ := shr(0x8, number_) lastBit := add(lastBit, 0x8) } if gt(number_, 0xF) { number_ := shr(0x4, number_) lastBit := add(lastBit, 0x4) } if gt(number_, 0x3) { number_ := shr(0x2, number_) lastBit := add(lastBit, 0x2) } if gt(number_, 0x1) { lastBit := add(lastBit, 1) } if gt(number_, 0) { lastBit := add(lastBit, 1) } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { BigMathMinified } from "./bigMathMinified.sol"; /// @title Extended version of BigMathMinified. Implements functions for normal operators (*, /, etc) modified to interact with big numbers. /// @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. // // @dev IMPORTANT: for any change here, make sure to uncomment and run the fuzz tests in bigMathVault.t.sol library BigMathVault { uint private constant COEFFICIENT_SIZE_DEBT_FACTOR = 35; uint private constant EXPONENT_SIZE_DEBT_FACTOR = 15; uint private constant COEFFICIENT_MAX_DEBT_FACTOR = (1 << COEFFICIENT_SIZE_DEBT_FACTOR) - 1; uint private constant EXPONENT_MAX_DEBT_FACTOR = (1 << EXPONENT_SIZE_DEBT_FACTOR) - 1; uint private constant DECIMALS_DEBT_FACTOR = 16384; uint internal constant MAX_MASK_DEBT_FACTOR = (1 << (COEFFICIENT_SIZE_DEBT_FACTOR + EXPONENT_SIZE_DEBT_FACTOR)) - 1; // Having precision as 2**64 on vault uint internal constant PRECISION = 64; uint internal constant TWO_POWER_64 = 1 << PRECISION; // Max bit for 35 bits * 35 bits number will be 70 // why do we use 69 then here instead of 70 uint internal constant TWO_POWER_69_MINUS_1 = (1 << 69) - 1; uint private constant COEFFICIENT_PLUS_PRECISION = COEFFICIENT_SIZE_DEBT_FACTOR + PRECISION; // 99 uint private constant COEFFICIENT_PLUS_PRECISION_MINUS_1 = COEFFICIENT_PLUS_PRECISION - 1; // 98 uint private constant TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1 = (1 << COEFFICIENT_PLUS_PRECISION_MINUS_1) - 1; // (1 << 98) - 1; uint private constant TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1_MINUS_1 = (1 << (COEFFICIENT_PLUS_PRECISION_MINUS_1 - 1)) - 1; // (1 << 97) - 1; /// @dev multiplies a `normal` number with a `bigNumber1` and then divides by `bigNumber2`. /// @dev For vault's use case MUST always: /// - bigNumbers have exponent size 15 bits /// - bigNumbers have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision) /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367. /// - bigNumber1 (debt factor) always have exponent >= 1 & <= 16384 /// - bigNumber2 (connection factor) always have exponent >= 1 & <= 32767 (15 bits) /// - bigNumber2 always >= bigNumber1 (connection factor can never be < base branch debt factor) /// - as a result of previous points, numbers must never be 0 /// - normal is positionRawDebt and is always within 10000 and type(int128).max /// @return normal * bigNumber1 / bigNumber2 function mulDivNormal(uint256 normal, uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) { unchecked { // exponent2_ - exponent1_ uint netExponent_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) - (bigNumber1 & EXPONENT_MAX_DEBT_FACTOR); if (netExponent_ < 129) { // (normal * coefficient1_) / (coefficient2_ << netExponent_); return ((normal * (bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR)) / ((bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR) << netExponent_)); } // else: // biggest possible nominator: type(int128).max * 35bits max = 5846006549323611672814739330865132078589370433536 // smallest possible denominator: 17179869184 << 129 (= 1 << 163) = 11692013098647223345629478661730264157247460343808 // -> can only ever be 0 return 0; } } /// @dev multiplies a `bigNumber` with normal `number1` and then divides by `TWO_POWER_64`. /// @dev For vault's use case (calculating new branch debt factor after liquidation): /// - number1 is debtFactor, intialized as TWO_POWER_64 and reduced from there, hence it's always <= TWO_POWER_64 and always > 0. /// - bigNumber is branch debt factor, which starts as ((X35 << 15) | (1 << 14)) and reduces from there. /// - bigNumber must have have exponent size 15 bits and be >= 1 & <= 16384 /// - bigNumber must have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision) /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367. /// @param bigNumber Coefficient | Exponent. /// @param number1 normal number. /// @return result bigNumber * number1 / TWO_POWER_64. function mulDivBigNumber(uint256 bigNumber, uint256 number1) internal pure returns (uint256 result) { // using unchecked as we are only at 1 place in Vault and it won't overflow there. unchecked { uint256 _resultNumerator = (bigNumber >> EXPONENT_SIZE_DEBT_FACTOR) * number1; // bigNumber coefficient * normal number // 99% chances are that most sig bit should be 64 + 35 - 1 or 64 + 35 - 2 // diff = mostSigBit. Can only ever be >= 35 and <= 98 uint256 diff = (_resultNumerator > TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1) ? COEFFICIENT_PLUS_PRECISION : (_resultNumerator > TWO_POWER_COEFFICIENT_PLUS_PRECISION_MINUS_1_MINUS_1) ? COEFFICIENT_PLUS_PRECISION_MINUS_1 : BigMathMinified.mostSignificantBit(_resultNumerator); // diff = difference in bits to make the _resultNumerator 35 bits again diff = diff - COEFFICIENT_SIZE_DEBT_FACTOR; _resultNumerator = _resultNumerator >> diff; // starting exponent is 16384, so exponent should never get 0 here result = (bigNumber & EXPONENT_MAX_DEBT_FACTOR) + diff; if (result > PRECISION) { result = (_resultNumerator << EXPONENT_SIZE_DEBT_FACTOR) + result - PRECISION; // divides by TWO_POWER_64 by reducing exponent by 64 } else { // if number1 is small, e.g. 1e4 and bigNumber is also small e.g. coefficient = 17179869184 & exponent is at 50 // then: resultNumerator = 171798691840000, diff most significant bit = 48, ending up with diff = 13 // for exponent in result we end up doing: 50 + 13 - 64 -> underflowing exponent. // this should never happen anyway, but if it does better to revert than to continue with unknown effects. revert(); // debt factor should never become a BigNumber with exponent <= 0 } } } /// @dev multiplies a `bigNumber1` with another `bigNumber2`. /// @dev For vault's use case (calculating connection factor of merged branches userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor): /// - bigNumbers must have have exponent size 15 bits and be >= 1 & <= 32767 /// - bigNumber must have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision) /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367. /// @dev sum of exponents from `bigNumber1` `bigNumber2` should be > 16384. /// e.g. res = bigNumber1 * bigNumber2 = [(coe1, exp1) * (coe2, exp2)] >> decimal /// = (coe1*coe2>>overflow, exp1+exp2+overflow-decimal) /// @param bigNumber1 BigNumber format with coefficient and exponent. /// @param bigNumber2 BigNumber format with coefficient and exponent. /// @return BigNumber format with coefficient and exponent function mulBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) { unchecked { // coefficient1_ * coefficient2_ uint resCoefficient_ = (bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) * (bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR); // res coefficient at min can be 17179869184 * 17179869184 = 295147905179352825856 (= 1 << 68; 69th bit as 1) // res coefficient at max can be 34359738367 * 34359738367 = 1180591620648691826689 (X35 * X35 fits in 70 bits) uint overflowLen_ = resCoefficient_ > TWO_POWER_69_MINUS_1 ? COEFFICIENT_SIZE_DEBT_FACTOR : COEFFICIENT_SIZE_DEBT_FACTOR - 1; // overflowLen_ is either 34 or 35 resCoefficient_ = resCoefficient_ >> overflowLen_; // bigNumber2 is connection factor // exponent1_ + exponent2_ + overflowLen_ - decimals uint resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) + (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) + overflowLen_); if (resExponent_ < DECIMALS_DEBT_FACTOR) { // for this ever to happen, the debt factors used to calculate connection factors would have to be at extremely // unrealistic values. Like e.g. // branch3 (debt factor X35 << 15 | 16383) got merged into branch2 (debt factor X35 << 15 | 8190) // -> connection factor (divBigNumber): ((coe1<<precision_)/coe2>>overflowLen, exp1+decimal+overflowLen-exp2-precision_) so: // coefficient: (X35<<64)/X35 >> 30 = 17179869184 // exponent: 8190+16384+30-16383-64 = 8157. // result: 17179869184 << 15 | 8157 // and then branch2 into branch1 (debt factor X35 << 15 | 22). -> connection factor: // coefficient: (X35<<64)/X35 >> 30 = 17179869184 // exponent: 22+16384+30-8190-64 = 8182. // result: 17179869184 << 15 | 8182 // connection factors sum up (mulBigNumber): (coe1*coe2>>overflow, exp1+exp2+overflow-decimal) // exponent: 8182+8157+35-16384=16374-16384=-10. underflow. // this should never happen anyway, but if it does better to revert than to continue with unknown effects. revert(); } resExponent_ = resExponent_ - DECIMALS_DEBT_FACTOR; if (resExponent_ > EXPONENT_MAX_DEBT_FACTOR) { // if resExponent_ is not within limits that means user's got ~100% (something like 99.999999999999...) // this situation will probably never happen and this basically means user's position is ~100% liquidated return MAX_MASK_DEBT_FACTOR; } return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_); } } /// @dev divides a `bigNumber1` by `bigNumber2`. /// @dev For vault's use case (calculating connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor) bigNumbers MUST always: /// - have exponent size 15 bits and be >= 1 & <= 16384 /// - have coefficient size 35 bits and have 35th bit always 1 (when exponent > 0 BigMath numbers have max precision) /// so coefficients must always be in range 17179869184 <= coefficient <= 34359738367. /// - as a result of previous points, numbers must never be 0 /// e.g. res = bigNumber1 / bigNumber2 = [(coe1, exp1) / (coe2, exp2)] << decimal /// = ((coe1<<precision_)/coe2, exp1+decimal-exp2-precision_) /// @param bigNumber1 BigNumber format with coefficient and exponent /// @param bigNumber2 BigNumber format with coefficient and exponent /// @return BigNumber format with coefficient and exponent /// 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) function divBigNumber(uint256 bigNumber1, uint256 bigNumber2) internal pure returns (uint256) { unchecked { // (coefficient1_ << PRECISION) / coefficient2_ uint256 resCoefficient_ = ((bigNumber1 >> EXPONENT_SIZE_DEBT_FACTOR) << PRECISION) / (bigNumber2 >> EXPONENT_SIZE_DEBT_FACTOR); // nominator at min 17179869184 << 64 = 316912650057057350374175801344. at max 34359738367 << 64 = 633825300095667956674642051072. // so min value resCoefficient_ 9223372037123211264 (64 bits) vs max 36893488146345361408 (fits in 65 bits) // mostSigBit will be PRECISION + 1 or PRECISION uint256 overflowLen_ = ((resCoefficient_ >> PRECISION) == 1) ? (PRECISION + 1) : PRECISION; // Overflow will be PRECISION - COEFFICIENT_SIZE_DEBT_FACTOR or (PRECISION + 1) - COEFFICIENT_SIZE_DEBT_FACTOR // Meaning 64 - 35 = 29 or 65 - 35 = 30 overflowLen_ = overflowLen_ - COEFFICIENT_SIZE_DEBT_FACTOR; resCoefficient_ = resCoefficient_ >> overflowLen_; // exponent1_ will always be less than or equal to 16384 // exponent2_ will always be less than or equal to 16384 // Even if exponent2_ is 0 (not possible) & resExponent_ = DECIMALS_DEBT_FACTOR then also resExponent_ will be less than max limit, so no overflow // result exponent = (exponent1_ + DECIMALS_DEBT_FACTOR + overflowLen_) - (exponent2_ + PRECISION); uint256 resExponent_ = ((bigNumber1 & EXPONENT_MAX_DEBT_FACTOR) + // exponent1_ DECIMALS_DEBT_FACTOR + // DECIMALS_DEBT_FACTOR is 100% as it is percentage value overflowLen_); // addition part resExponent_ here min 16414, max 32798 // reuse overFlowLen_ variable for subtraction sum of exponent overflowLen_ = (bigNumber2 & EXPONENT_MAX_DEBT_FACTOR) + PRECISION; // subtraction part overflowLen_ here: min 65, max 16448 if (resExponent_ > overflowLen_) { resExponent_ = resExponent_ - overflowLen_; return ((resCoefficient_ << EXPONENT_SIZE_DEBT_FACTOR) | resExponent_); } // Can happen if bigNumber1 exponent is < 35 (35+16384+29 = 16448) and bigNumber2 exponent is e.g. max 16384. // this would mean a branch with a normal big debt factor (bigNumber2) is merged into a base branch with an extremely small // debt factor (bigNumber1). // this should never happen anyway, but if it does better to revert than to continue with unknown effects. revert(); // connection factor should never become a BigNumber with exponent <= 0 } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; library LibsErrorTypes { /***********************************| | LiquidityCalcs | |__________________________________*/ /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet) uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001; /// @notice thrown when rate data is set to a version that is not implemented uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002; /// @notice thrown when the calculated borrow rate turns negative. This should never happen. uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003; /***********************************| | SafeTransfer | |__________________________________*/ /// @notice thrown when safe transfer from for an ERC20 fails uint256 internal constant SafeTransfer__TransferFromFailed = 71001; /// @notice thrown when safe transfer for an ERC20 fails uint256 internal constant SafeTransfer__TransferFailed = 71002; /***********************************| | SafeApprove | |__________________________________*/ /// @notice thrown when safe approve from for an ERC20 fails uint256 internal constant SafeApprove__ApproveFailed = 81001; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; interface IFluidProtocol { function TYPE() external view returns (uint256); } /// @notice implements helper methods to filter Fluid protocols by a certain type library FluidProtocolTypes { uint256 internal constant VAULT_T1_TYPE = 10000; // VaultT1 borrow protocol type vaults uint256 internal constant VAULT_T2_SMART_COL_TYPE = 20000; // DEX protocol type vault uint256 internal constant VAULT_T3_SMART_DEBT_TYPE = 30000; // DEX protocol type vault uint256 internal constant VAULT_T4_SMART_COL_SMART_DEBT_TYPE = 40000; // DEX protocol type vault /// @dev filters input `addresses_` by protocol `type_`. Input addresses must be actual Fluid protocols, otherwise /// they would be wrongly assumed to be VaultT1 even if they are not Fluid VaultT1 smart contracts. /// `type_` must be a listed constant type of this library. /// Example usage is to filter all vault addresses at the Vault factory by a certain type, e.g. to not include /// DEX protocol type vaults. function filterBy(address[] memory addresses_, uint256 type_) internal view returns (address[] memory filtered_) { uint256 curType_; uint256 filteredProtocols_ = addresses_.length; for (uint256 i; i < addresses_.length; ) { try IFluidProtocol(addresses_[i]).TYPE() returns (uint256 protocolType_) { curType_ = protocolType_; } catch { curType_ = VAULT_T1_TYPE; } if (curType_ != type_) { addresses_[i] = address(0); --filteredProtocols_; } unchecked { ++i; } } filtered_ = new address[](filteredProtocols_); uint256 index_; unchecked { for (uint256 i; i < addresses_.length; ) { if (addresses_[i] != address(0)) { filtered_[index_] = addresses_[i]; ++index_; } ++i; } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol"; import { LiquiditySlotsLink } from "./liquiditySlotsLink.sol"; import { BigMathMinified } from "./bigMathMinified.sol"; /// @notice implements calculation methods used for Fluid liquidity such as updated exchange prices, /// borrow rate, withdrawal / borrow limits, revenue amount. library LiquidityCalcs { error FluidLiquidityCalcsError(uint256 errorId_); /// @notice emitted if the calculated borrow rate surpassed max borrow rate (16 bits) and was capped at maximum value 65535 event BorrowRateMaxCap(); /// @dev constants as from Liquidity variables.sol uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12; /// @dev Ignoring leap years uint256 internal constant SECONDS_PER_YEAR = 365 days; // constants used for BigMath conversion from and to storage uint256 internal constant DEFAULT_EXPONENT_SIZE = 8; uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF; uint256 internal constant FOUR_DECIMALS = 1e4; uint256 internal constant TWELVE_DECIMALS = 1e12; uint256 internal constant X14 = 0x3fff; uint256 internal constant X15 = 0x7fff; uint256 internal constant X16 = 0xffff; uint256 internal constant X18 = 0x3ffff; uint256 internal constant X24 = 0xffffff; uint256 internal constant X33 = 0x1ffffffff; uint256 internal constant X64 = 0xffffffffffffffff; /////////////////////////////////////////////////////////////////////////// ////////// CALC EXCHANGE PRICES ///////// /////////////////////////////////////////////////////////////////////////// /// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage. /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage /// @return supplyExchangePrice_ updated supplyExchangePrice /// @return borrowExchangePrice_ updated borrowExchangePrice function calcExchangePrices( uint256 exchangePricesAndConfig_ ) internal view returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) { // Extracting exchange prices supplyExchangePrice_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64; borrowExchangePrice_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64; if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) { revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__ExchangePriceZero); } uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate unchecked { // last timestamp can not be > current timestamp uint256 secondsSinceLastUpdate_ = block.timestamp - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33); uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) & X15; if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) { // if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed // (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0) return (supplyExchangePrice_, borrowExchangePrice_); } // calculate new borrow exchange price. // formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_. // nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0. borrowExchangePrice_ += (borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) / (SECONDS_PER_YEAR * FOUR_DECIMALS); // FOR SUPPLY EXCHANGE PRICE: // all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest. // formula: previous price * supply rate * secondsSinceLastUpdate_. // where supply rate = (borrow rate - revenueFee%) * ratioSupplyYield. And // ratioSupplyYield = utilization * supplyRatio * borrowRatio // // Example: // supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50. // BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%. // yield is 10 (so half a year must have passed). // supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60. // borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70. // supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2). // utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield). // borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield): // x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%. // ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5% // supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5% // increase in supplyExchangePrice, assuming 100 as previous price. // 100 * 22,5% * 1/2 (half a year) = 0,1125. // cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20. // -------------- 1. calculate ratioSupplyYield -------------------------------- // step1: utilization * supplyRatio (or actually part of lenders receiving yield) // temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383) // if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger) // else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger) temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15; if (temp_ == 1) { // if no raw supply: no exchange price update needed // (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0) return (supplyExchangePrice_, borrowExchangePrice_); } // ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest if (temp_ & 1 == 1) { // ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger) temp_ = temp_ >> 1; // Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return // in the if statement a little above. // based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee. // supplyRawInterest must become worth 30. totalSupply must become 110. // supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2. // so ratioSupplyYield must come out as 2.5 (250%). // supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted. temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision) // e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%). temp_ = // utilization * (100% + 100% / supplyRatio) (((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * (1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow). (FOUR_DECIMALS); // max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31 } else { // ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger) temp_ = temp_ >> 1; // if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0 // e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%). temp_ = // 1e27 * utilization * (100% + supplyRatio) / 100% (1e27 * ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow). (FOUR_DECIMALS + temp_)) / (FOUR_DECIMALS * FOUR_DECIMALS); // max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27 } // from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31 // step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield) if (borrowRatio_ & 1 == 1) { // ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger) borrowRatio_ = borrowRatio_ >> 1; // borrowRatio_ => x of total bororwers paying yield. scale to 1e27. // Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered // at the beginning of the method by early return if `borrowRatio_ == 1`. // based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%. // so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed. // x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666% // borrowRatio_ => x of total bororwers paying yield. scale to 1e27. borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_); // max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield). } else { // ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger) borrowRatio_ = borrowRatio_ >> 1; // borrowRatio_ => x of total bororwers paying yield. scale to 1e27. // x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%. borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_))); // borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%. // or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101. // max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield). } // temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%. // max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8 temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54; // 2. calculate supply rate // temp_ => supply rate (borrow rate - revenueFee%) * ratioSupplyYield. // division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate) // Note that all calculation divisions for supplyExchangePrice are rounded down. // Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest // but more suppliers not earning interest. temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate temp_ * // ratioSupplyYield (FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee // fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17. // 3. calculate increase in supply exchange price supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) / (SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS)); // max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0. } } /////////////////////////////////////////////////////////////////////////// ////////// CALC REVENUE ///////// /////////////////////////////////////////////////////////////////////////// /// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage /// and the current balance of the Fluid liquidity contract for the token. /// @param totalAmounts_ total amounts packed uint256 read from storage /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this))) /// @return revenueAmount_ collectable revenue amount function calcRevenue( uint256 totalAmounts_, uint256 exchangePricesAndConfig_, uint256 liquidityTokenBalance_ ) internal view returns (uint256 revenueAmount_) { // @dev no need to super-optimize this method as it is only used by admin // calculate the new exchange prices based on earned interest (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(exchangePricesAndConfig_); // total supply = interest free + with interest converted from raw uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_); if (totalSupply_ > 0) { // available revenue: balanceOf(token) + totalBorrowings - totalLendings. revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_); // ensure there is no possible case because of rounding etc. where this would revert, // explicitly check if > revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0; // Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization // can only be revenue. } else { // if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck revenueAmount_ = liquidityTokenBalance_; } } /////////////////////////////////////////////////////////////////////////// ////////// CALC LIMITS ///////// /////////////////////////////////////////////////////////////////////////// /// @dev calculates withdrawal limit before an operate execution: /// amount of user supply that must stay supplied (not amount that can be withdrawn). /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M /// @param userSupplyData_ user supply data packed uint256 from storage /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction. /// returned value is in raw for with interest mode, normal amount for interest free mode! function calcWithdrawalLimitBeforeOperate( uint256 userSupplyData_, uint256 userSupply_ ) internal view returns (uint256 currentWithdrawalLimit_) { // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet). // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet. // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be // a deposit anyway. Important is that it would not revert. // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit // is the fully expanded limit immediately. // extract last set withdrawal limit uint256 lastWithdrawalLimit_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) & X64; lastWithdrawalLimit_ = (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) << (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK); if (lastWithdrawalLimit_ == 0) { // withdrawal limit is not activated. Max withdrawal allowed return 0; } uint256 maxWithdrawableLimit_; uint256 temp_; unchecked { // extract max withdrawable percent of user supply and // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed. // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible). maxWithdrawableLimit_ = (((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) / FOUR_DECIMALS; // time elapsed since last withdrawal limit was set (in seconds) // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before. // last timestamp can not be > current timestamp temp_ = block.timestamp - ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33); } // calculate withdrawable amount of expandPercent that is elapsed of expandDuration. // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%. // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed. temp_ = (maxWithdrawableLimit_ * temp_) / // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit) ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0 // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount. // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration, // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0 // which will cause minimum (fully expanded) withdrawal limit to be set in lines below. unchecked { // underflow explicitly checked & handled currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0; // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion. // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_ temp_ = userSupply_ - maxWithdrawableLimit_; } // if withdrawal limit is decreased below minimum then set minimum // (e.g. when more than expandDuration time has elapsed) if (temp_ > currentWithdrawalLimit_) { currentWithdrawalLimit_ = temp_; } } /// @dev calculates withdrawal limit after an operate execution: /// amount of user supply that must stay supplied (not amount that can be withdrawn). /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M /// @param userSupplyData_ user supply data packed uint256 from storage /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate` /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in /// raw for with interest mode, normal amount for interest free mode! function calcWithdrawalLimitAfterOperate( uint256 userSupplyData_, uint256 userSupply_, uint256 newWithdrawalLimit_ ) internal pure returns (uint256) { // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed uint256 temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); // if user supply is below base limit then max withdrawals are allowed if (userSupply_ < temp_) { return 0; } // temp_ => withdrawal limit expandPercent (is in 1e2 decimals) temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14; unchecked { // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent)) // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible). // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_ temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS); } // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit. // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where // increased deposit amount outpaces withrawals. if (temp_ > newWithdrawalLimit_) { return temp_; } return newWithdrawalLimit_; } /// @dev calculates borrow limit before an operate execution: /// total amount user borrow can reach (not borrowable amount in current operation). /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M /// @param userBorrowData_ user borrow data packed uint256 from storage /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in /// raw for with interest mode, normal amount for interest free mode! function calcBorrowLimitBeforeOperate( uint256 userBorrowData_, uint256 userBorrow_ ) internal view returns (uint256 currentBorrowLimit_) { // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit. // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0. // temp_ = extract borrow expand percent (is in 1e2 decimals) uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; uint256 maxExpansionLimit_; uint256 maxExpandedBorrowLimit_; unchecked { // calculate max expansion limit: Max amount limit can expand to since last interaction // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible). maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS); // calculate max borrow limit: Max point limit can increase to since last interaction maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_; } // currentBorrowLimit_ = extract base borrow limit currentBorrowLimit_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18; currentBorrowLimit_ = (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) << (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK); if (maxExpandedBorrowLimit_ < currentBorrowLimit_) { return currentBorrowLimit_; } // time elapsed since last borrow limit was set (in seconds) unchecked { // temp_ = timeElapsed_ (last timestamp can not be > current timestamp) temp_ = block.timestamp - ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last update timestamp } // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit currentBorrowLimit_ = // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`. // divisor is extract expand duration (after this, full expansion to expandPercentage happened). ((maxExpansionLimit_ * temp_) / ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0 // extract last set borrow limit BigMathMinified.fromBigNumber( (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64, DEFAULT_EXPONENT_SIZE, DEFAULT_EXPONENT_MASK ); // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion, // so set to `maxExpandedBorrowLimit_` in that case. // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big) if (currentBorrowLimit_ > maxExpandedBorrowLimit_) { currentBorrowLimit_ = maxExpandedBorrowLimit_; } // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above) temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); if (currentBorrowLimit_ > temp_) { currentBorrowLimit_ = temp_; } } /// @dev calculates borrow limit after an operate execution: /// total amount user borrow can reach (not borrowable amount in current operation). /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M /// @param userBorrowData_ user borrow data packed uint256 from storage /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate` /// @return borrowLimit_ updated borrow limit that should be written to storage. /// returned value is in raw for with interest mode, normal amount for interest free mode! function calcBorrowLimitAfterOperate( uint256 userBorrowData_, uint256 userBorrow_, uint256 newBorrowLimit_ ) internal pure returns (uint256 borrowLimit_) { // temp_ = extract borrow expand percent uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals) unchecked { // borrowLimit_ = calculate maximum borrow limit at full expansion. // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible). borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS); } // temp_ = extract base borrow limit temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); if (borrowLimit_ < temp_) { // below base limit, borrow limit is always base limit return temp_; } // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above) temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); // make sure fully expanded borrow limit is not above hard max borrow limit if (borrowLimit_ > temp_) { borrowLimit_ = temp_; } // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit. // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant) if (newBorrowLimit_ > borrowLimit_) { return borrowLimit_; } return newBorrowLimit_; } /////////////////////////////////////////////////////////////////////////// ////////// CALC RATES ///////// /////////////////////////////////////////////////////////////////////////// /// @dev Calculates new borrow rate from utilization for a token /// @param rateData_ rate data packed uint256 from storage for the token /// @param utilization_ totalBorrow / totalSupply. 1e4 = 100% utilization /// @return rate_ rate for that particular token in 1e2 precision (e.g. 5% rate = 500) function calcBorrowRateFromUtilization(uint256 rateData_, uint256 utilization_) internal returns (uint256 rate_) { // extract rate version: 4 bits (0xF) starting from bit 0 uint256 rateVersion_ = (rateData_ & 0xF); if (rateVersion_ == 1) { rate_ = calcRateV1(rateData_, utilization_); } else if (rateVersion_ == 2) { rate_ = calcRateV2(rateData_, utilization_); } else { revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__UnsupportedRateVersion); } if (rate_ > X16) { // hard cap for borrow rate at maximum value 16 bits (65535) to make sure it does not overflow storage space. // this is unlikely to ever happen if configs stay within expected levels. rate_ = X16; // emit event to more easily become aware emit BorrowRateMaxCap(); } } /// @dev calculates the borrow rate based on utilization for rate data version 1 (with one kink) in 1e2 precision /// @param rateData_ rate data packed uint256 from storage for the token /// @param utilization_ in 1e2 (100% = 1e4) /// @return rate_ rate in 1e2 precision function calcRateV1(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) { /// For rate v1 (one kink) ------------------------------------------------------ /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 52- 67 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Last 188 bits => 68-255 => blank, might come in use in future // y = mx + c. // y is borrow rate // x is utilization // m = slope (m can also be negative for declining rates) // c is constant (c can be negative) uint256 y1_; uint256 y2_; uint256 x1_; uint256 x2_; // extract kink1: 16 bits (0xFFFF) starting from bit 20 // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16; if (utilization_ < kink1_) { // if utilization is less than kink y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16; x1_ = 0; // 0% x2_ = kink1_; } else { // else utilization is greater than kink y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) & X16; x1_ = kink1_; x2_ = FOUR_DECIMALS; // 100% } int256 constant_; int256 slope_; unchecked { // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1). // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor) // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_)); // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx. // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256 // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12; // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256 // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256 constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_)); // calculating new borrow rate // - slope_ max value is 65535 * 1e12, // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply) // - constant max value is 65535 * 1e12 // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256 // divisor TWELVE_DECIMALS can not be 0 slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings if (slope_ < 0) { revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative); } rate_ = uint256(slope_) / TWELVE_DECIMALS; } } /// @dev calculates the borrow rate based on utilization for rate data version 2 (with two kinks) in 1e4 precision /// @param rateData_ rate data packed uint256 from storage for the token /// @param utilization_ in 1e2 (100% = 1e4) /// @return rate_ rate in 1e4 precision function calcRateV2(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) { /// For rate v2 (two kinks) ----------------------------------------------------- /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 52- 67 => Utilization at kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 68- 83 => Rate at utilization kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 84- 99 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Last 156 bits => 100-255 => blank, might come in use in future // y = mx + c. // y is borrow rate // x is utilization // m = slope (m can also be negative for declining rates) // c is constant (c can be negative) uint256 y1_; uint256 y2_; uint256 x1_; uint256 x2_; // extract kink1: 16 bits (0xFFFF) starting from bit 20 // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) & X16; if (utilization_ < kink1_) { // if utilization is less than kink1 y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16; x1_ = 0; // 0% x2_ = kink1_; } else { // extract kink2: 16 bits (0xFFFF) starting from bit 52 uint256 kink2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) & X16; if (utilization_ < kink2_) { // if utilization is less than kink2 y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16; x1_ = kink1_; x2_ = kink2_; } else { // else utilization is greater than kink2 y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) & X16; x1_ = kink2_; x2_ = FOUR_DECIMALS; } } int256 constant_; int256 slope_; unchecked { // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1). // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor) // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_)); // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx. // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256 // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12; // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256 // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256 constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_)); // calculating new borrow rate // - slope_ max value is 65535 * 1e12, // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply) // - constant max value is 65535 * 1e12 // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256 // divisor TWELVE_DECIMALS can not be 0 slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings if (slope_ < 0) { revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative); } rate_ = uint256(slope_) / TWELVE_DECIMALS; } } /// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_` function getTotalSupply( uint256 totalAmounts_, uint256 supplyExchangePrice_ ) internal pure returns (uint256 totalSupply_) { // totalSupply_ => supplyInterestFree totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64; totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK); uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK); // totalSupply = supplyInterestFree + supplyRawInterest normalized from raw totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION); } /// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_` function getTotalBorrow( uint256 totalAmounts_, uint256 borrowExchangePrice_ ) internal pure returns (uint256 totalBorrow_) { // totalBorrow_ => borrowInterestFree // no & mask needed for borrow interest free as it occupies the last bits in the storage slot totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE); totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK); uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64; totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK); // totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; /// @notice library that helps in reading / working with storage slot data of Fluid Liquidity. /// @dev as all data for Fluid Liquidity is internal, any data must be fetched directly through manual /// slot reading through this library or, if gas usage is less important, through the FluidLiquidityResolver. library LiquiditySlotsLink { /// @dev storage slot for status at Liquidity uint256 internal constant LIQUIDITY_STATUS_SLOT = 1; /// @dev storage slot for auths mapping at Liquidity uint256 internal constant LIQUIDITY_AUTHS_MAPPING_SLOT = 2; /// @dev storage slot for guardians mapping at Liquidity uint256 internal constant LIQUIDITY_GUARDIANS_MAPPING_SLOT = 3; /// @dev storage slot for user class mapping at Liquidity uint256 internal constant LIQUIDITY_USER_CLASS_MAPPING_SLOT = 4; /// @dev storage slot for exchangePricesAndConfig mapping at Liquidity uint256 internal constant LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT = 5; /// @dev storage slot for rateData mapping at Liquidity uint256 internal constant LIQUIDITY_RATE_DATA_MAPPING_SLOT = 6; /// @dev storage slot for totalAmounts mapping at Liquidity uint256 internal constant LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT = 7; /// @dev storage slot for user supply double mapping at Liquidity uint256 internal constant LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT = 8; /// @dev storage slot for user borrow double mapping at Liquidity uint256 internal constant LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT = 9; /// @dev storage slot for listed tokens array at Liquidity uint256 internal constant LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT = 10; /// @dev storage slot for listed tokens array at Liquidity uint256 internal constant LIQUIDITY_CONFIGS2_MAPPING_SLOT = 11; // -------------------------------- // @dev stacked uint256 storage slots bits position data for each: // ExchangePricesAndConfig uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATE = 0; uint256 internal constant BITS_EXCHANGE_PRICES_FEE = 16; uint256 internal constant BITS_EXCHANGE_PRICES_UTILIZATION = 30; uint256 internal constant BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD = 44; uint256 internal constant BITS_EXCHANGE_PRICES_LAST_TIMESTAMP = 58; uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE = 91; uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE = 155; uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_RATIO = 219; uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATIO = 234; uint256 internal constant BITS_EXCHANGE_PRICES_USES_CONFIGS2 = 249; // RateData: uint256 internal constant BITS_RATE_DATA_VERSION = 0; // RateData: V1 uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO = 4; uint256 internal constant BITS_RATE_DATA_V1_UTILIZATION_AT_KINK = 20; uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK = 36; uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX = 52; // RateData: V2 uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO = 4; uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1 = 20; uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1 = 36; uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2 = 52; uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2 = 68; uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX = 84; // TotalAmounts uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_WITH_INTEREST = 0; uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE = 64; uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST = 128; uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE = 192; // UserSupplyData uint256 internal constant BITS_USER_SUPPLY_MODE = 0; uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1; uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65; uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129; uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162; uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176; uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200; uint256 internal constant BITS_USER_SUPPLY_IS_PAUSED = 255; // UserBorrowData uint256 internal constant BITS_USER_BORROW_MODE = 0; uint256 internal constant BITS_USER_BORROW_AMOUNT = 1; uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65; uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129; uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162; uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176; uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200; uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218; uint256 internal constant BITS_USER_BORROW_IS_PAUSED = 255; // Configs2 uint256 internal constant BITS_CONFIGS2_MAX_UTILIZATION = 0; // -------------------------------- /// @notice Calculating the slot ID for Liquidity contract for single mapping at `slot_` for `key_` function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) { return keccak256(abi.encode(key_, slot_)); } /// @notice Calculating the slot ID for Liquidity contract for double mapping at `slot_` for `key1_` and `key2_` function calculateDoubleMappingStorageSlot( uint256 slot_, address key1_, address key2_ ) internal pure returns (bytes32) { bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_)); return keccak256(abi.encode(key2_, intermediateSlot_)); } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.21; import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol"; /// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom library SafeTransfer { uint256 internal constant MAX_NATIVE_TRANSFER_GAS = 20000; // pass max. 20k gas for native transfers error FluidSafeTransferError(uint256 errorId_); /// @dev Transfer `amount_` of `token_` from `from_` to `to_`, spending the approval given by `from_` to the /// calling contract. If `token_` returns no value, non-reverting calls are assumed to be successful. /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error): /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63 function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal { bool success_; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from_" argument. mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument. mstore(add(freeMemoryPointer, 68), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type. success_ := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32) ) } if (!success_) { revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed); } } /// @dev Transfer `amount_` of `token_` to `to_`. /// If `token_` returns no value, non-reverting calls are assumed to be successful. /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error): /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95 function safeTransfer(address token_, address to_, uint256 amount_) internal { bool success_; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument. mstore(add(freeMemoryPointer, 36), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type. success_ := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32) ) } if (!success_) { revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed); } } /// @dev Transfer `amount_` of ` native token to `to_`. /// Minimally modified from Solmate SafeTransferLib (Custom Error): /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25 function safeTransferNative(address to_, uint256 amount_) internal { bool success_; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. Pass limited gas success_ := call(MAX_NATIVE_TRANSFER_GAS, to_, amount_, 0, 0, 0, 0) } if (!success_) { revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; /// @notice implements a method to read uint256 data from storage at a bytes32 storage slot key. contract StorageRead { function readFromStorage(bytes32 slot_) public view returns (uint256 result_) { assembly { result_ := sload(slot_) // read value from the storage slot } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; /// @title library that calculates number "tick" and "ratioX96" from this: ratioX96 = (1.0015^tick) * 2^96 /// @notice this library is used in Fluid Vault protocol for optimiziation. /// @dev "tick" supports between -32767 and 32767. "ratioX96" supports between 37075072 and 169307877264527972847801929085841449095838922544595 library TickMath { /// The minimum tick that can be passed in getRatioAtTick. 1.0015**-32767 int24 internal constant MIN_TICK = -32767; /// The maximum tick that can be passed in getRatioAtTick. 1.0015**32767 int24 internal constant MAX_TICK = 32767; uint256 internal constant FACTOR00 = 0x100000000000000000000000000000000; uint256 internal constant FACTOR01 = 0xff9dd7de423466c20352b1246ce4856f; // 2^128/1.0015**1 = 339772707859149738855091969477551883631 uint256 internal constant FACTOR02 = 0xff3bd55f4488ad277531fa1c725a66d0; // 2^128/1.0015**2 = 339263812140938331358054887146831636176 uint256 internal constant FACTOR03 = 0xfe78410fd6498b73cb96a6917f853259; // 2^128/1.0015**4 = 338248306163758188337119769319392490073 uint256 internal constant FACTOR04 = 0xfcf2d9987c9be178ad5bfeffaa123273; // 2^128/1.0015**8 = 336226404141693512316971918999264834163 uint256 internal constant FACTOR05 = 0xf9ef02c4529258b057769680fc6601b3; // 2^128/1.0015**16 = 332218786018727629051611634067491389875 uint256 internal constant FACTOR06 = 0xf402d288133a85a17784a411f7aba082; // 2^128/1.0015**32 = 324346285652234375371948336458280706178 uint256 internal constant FACTOR07 = 0xe895615b5beb6386553757b0352bda90; // 2^128/1.0015**64 = 309156521885964218294057947947195947664 uint256 internal constant FACTOR08 = 0xd34f17a00ffa00a8309940a15930391a; // 2^128/1.0015**128 = 280877777739312896540849703637713172762 uint256 internal constant FACTOR09 = 0xae6b7961714e20548d88ea5123f9a0ff; // 2^128/1.0015**256 = 231843708922198649176471782639349113087 uint256 internal constant FACTOR10 = 0x76d6461f27082d74e0feed3b388c0ca1; // 2^128/1.0015**512 = 157961477267171621126394973980180876449 uint256 internal constant FACTOR11 = 0x372a3bfe0745d8b6b19d985d9a8b85bb; // 2^128/1.0015**1024 = 73326833024599564193373530205717235131 uint256 internal constant FACTOR12 = 0x0be32cbee48979763cf7247dd7bb539d; // 2^128/1.0015**2048 = 15801066890623697521348224657638773661 uint256 internal constant FACTOR13 = 0x8d4f70c9ff4924dac37612d1e2921e; // 2^128/1.0015**4096 = 733725103481409245883800626999235102 uint256 internal constant FACTOR14 = 0x4e009ae5519380809a02ca7aec77; // 2^128/1.0015**8192 = 1582075887005588088019997442108535 uint256 internal constant FACTOR15 = 0x17c45e641b6e95dee056ff10; // 2^128/1.0015**16384 = 7355550435635883087458926352 /// The minimum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MIN_TICK). ~ Equivalent to `(1 << 96) * (1.0015**-32767)` uint256 internal constant MIN_RATIOX96 = 37075072; /// The maximum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MAX_TICK). /// ~ Equivalent to `(1 << 96) * (1.0015**32767)`, rounding etc. leading to minor difference uint256 internal constant MAX_RATIOX96 = 169307877264527972847801929085841449095838922544595; uint256 internal constant ZERO_TICK_SCALED_RATIO = 0x1000000000000000000000000; // 1 << 96 // 79228162514264337593543950336 uint256 internal constant _1E26 = 1e26; /// @notice ratioX96 = (1.0015^tick) * 2^96 /// @dev Throws if |tick| > max tick /// @param tick The input tick for the above formula /// @return ratioX96 ratio = (debt amount/collateral amount) function getRatioAtTick(int tick) internal pure returns (uint256 ratioX96) { assembly { let absTick_ := sub(xor(tick, sar(255, tick)), sar(255, tick)) if gt(absTick_, MAX_TICK) { revert(0, 0) } let factor_ := FACTOR00 if and(absTick_, 0x1) { factor_ := FACTOR01 } if and(absTick_, 0x2) { factor_ := shr(128, mul(factor_, FACTOR02)) } if and(absTick_, 0x4) { factor_ := shr(128, mul(factor_, FACTOR03)) } if and(absTick_, 0x8) { factor_ := shr(128, mul(factor_, FACTOR04)) } if and(absTick_, 0x10) { factor_ := shr(128, mul(factor_, FACTOR05)) } if and(absTick_, 0x20) { factor_ := shr(128, mul(factor_, FACTOR06)) } if and(absTick_, 0x40) { factor_ := shr(128, mul(factor_, FACTOR07)) } if and(absTick_, 0x80) { factor_ := shr(128, mul(factor_, FACTOR08)) } if and(absTick_, 0x100) { factor_ := shr(128, mul(factor_, FACTOR09)) } if and(absTick_, 0x200) { factor_ := shr(128, mul(factor_, FACTOR10)) } if and(absTick_, 0x400) { factor_ := shr(128, mul(factor_, FACTOR11)) } if and(absTick_, 0x800) { factor_ := shr(128, mul(factor_, FACTOR12)) } if and(absTick_, 0x1000) { factor_ := shr(128, mul(factor_, FACTOR13)) } if and(absTick_, 0x2000) { factor_ := shr(128, mul(factor_, FACTOR14)) } if and(absTick_, 0x4000) { factor_ := shr(128, mul(factor_, FACTOR15)) } let precision_ := 0 if iszero(and(tick, 0x8000000000000000000000000000000000000000000000000000000000000000)) { factor_ := div(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, factor_) // we round up in the division so getTickAtRatio of the output price is always consistent if mod(factor_, 0x100000000) { precision_ := 1 } } ratioX96 := add(shr(32, factor_), precision_) } } /// @notice ratioX96 = (1.0015^tick) * 2^96 /// @dev Throws if ratioX96 > max ratio || ratioX96 < min ratio /// @param ratioX96 The input ratio; ratio = (debt amount/collateral amount) /// @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 /// @return perfectRatioX96 perfect ratio for the above tick function getTickAtRatio(uint256 ratioX96) internal pure returns (int tick, uint perfectRatioX96) { assembly { if or(gt(ratioX96, MAX_RATIOX96), lt(ratioX96, MIN_RATIOX96)) { revert(0, 0) } let cond := lt(ratioX96, ZERO_TICK_SCALED_RATIO) let factor_ if iszero(cond) { // if ratioX96 >= ZERO_TICK_SCALED_RATIO factor_ := div(mul(ratioX96, _1E26), ZERO_TICK_SCALED_RATIO) } if cond { // ratioX96 < ZERO_TICK_SCALED_RATIO factor_ := div(mul(ZERO_TICK_SCALED_RATIO, _1E26), ratioX96) } // put in https://www.wolframalpha.com/ whole equation: (1.0015^tick) * 2^96 * 10^26 / 79228162514264337593543950336 // for tick = 16384 // ratioX96 = (1.0015^16384) * 2^96 = 3665252098134783297721995888537077351735 // 3665252098134783297721995888537077351735 * 10^26 / 79228162514264337593543950336 = // 4626198540796508716348404308345255985.06131964639489434655721 if iszero(lt(factor_, 4626198540796508716348404308345255985)) { tick := or(tick, 0x4000) factor_ := div(mul(factor_, _1E26), 4626198540796508716348404308345255985) } // for tick = 8192 // ratioX96 = (1.0015^8192) * 2^96 = 17040868196391020479062776466509865 // 17040868196391020479062776466509865 * 10^26 / 79228162514264337593543950336 = // 21508599537851153911767490449162.3037648642153898377655505172 if iszero(lt(factor_, 21508599537851153911767490449162)) { tick := or(tick, 0x2000) factor_ := div(mul(factor_, _1E26), 21508599537851153911767490449162) } // for tick = 4096 // ratioX96 = (1.0015^4096) * 2^96 = 36743933851015821532611831851150 // 36743933851015821532611831851150 * 10^26 / 79228162514264337593543950336 = // 46377364670549310883002866648.9777607649742626173648716941385 if iszero(lt(factor_, 46377364670549310883002866649)) { tick := or(tick, 0x1000) factor_ := div(mul(factor_, _1E26), 46377364670549310883002866649) } // for tick = 2048 // ratioX96 = (1.0015^2048) * 2^96 = 1706210527034005899209104452335 // 1706210527034005899209104452335 * 10^26 / 79228162514264337593543950336 = // 2153540449365864845468344760.06357108484096046743300420319322 if iszero(lt(factor_, 2153540449365864845468344760)) { tick := or(tick, 0x800) factor_ := div(mul(factor_, _1E26), 2153540449365864845468344760) } // for tick = 1024 // ratioX96 = (1.0015^1024) * 2^96 = 367668226692760093024536487236 // 367668226692760093024536487236 * 10^26 / 79228162514264337593543950336 = // 464062544207767844008185024.950588990554136265212906454481127 if iszero(lt(factor_, 464062544207767844008185025)) { tick := or(tick, 0x400) factor_ := div(mul(factor_, _1E26), 464062544207767844008185025) } // for tick = 512 // ratioX96 = (1.0015^512) * 2^96 = 170674186729409605620119663668 // 170674186729409605620119663668 * 10^26 / 79228162514264337593543950336 = // 215421109505955298802281577.031879604792139232258508172947569 if iszero(lt(factor_, 215421109505955298802281577)) { tick := or(tick, 0x200) factor_ := div(mul(factor_, _1E26), 215421109505955298802281577) } // for tick = 256 // ratioX96 = (1.0015^256) * 2^96 = 116285004205991934861656513301 // 116285004205991934861656513301 * 10^26 / 79228162514264337593543950336 = // 146772309890508740607270614.667650899656438875541505058062410 if iszero(lt(factor_, 146772309890508740607270615)) { tick := or(tick, 0x100) factor_ := div(mul(factor_, _1E26), 146772309890508740607270615) } // for tick = 128 // ratioX96 = (1.0015^128) * 2^96 = 95984619659632141743747099590 // 95984619659632141743747099590 * 10^26 / 79228162514264337593543950336 = // 121149622323187099817270416.157248837742741760456796835775887 if iszero(lt(factor_, 121149622323187099817270416)) { tick := or(tick, 0x80) factor_ := div(mul(factor_, _1E26), 121149622323187099817270416) } // for tick = 64 // ratioX96 = (1.0015^64) * 2^96 = 87204845308406958006717891124 // 87204845308406958006717891124 * 10^26 / 79228162514264337593543950336 = // 110067989135437147685980801.568068573422377364214113968609839 if iszero(lt(factor_, 110067989135437147685980801)) { tick := or(tick, 0x40) factor_ := div(mul(factor_, _1E26), 110067989135437147685980801) } // for tick = 32 // ratioX96 = (1.0015^32) * 2^96 = 83120873769022354029916374475 // 83120873769022354029916374475 * 10^26 / 79228162514264337593543950336 = // 104913292358707887270979599.831816586773651266562785765558183 if iszero(lt(factor_, 104913292358707887270979600)) { tick := or(tick, 0x20) factor_ := div(mul(factor_, _1E26), 104913292358707887270979600) } // for tick = 16 // ratioX96 = (1.0015^16) * 2^96 = 81151180492336368327184716176 // 81151180492336368327184716176 * 10^26 / 79228162514264337593543950336 = // 102427189924701091191840927.762844039579442328381455567932128 if iszero(lt(factor_, 102427189924701091191840928)) { tick := or(tick, 0x10) factor_ := div(mul(factor_, _1E26), 102427189924701091191840928) } // for tick = 8 // ratioX96 = (1.0015^8) * 2^96 = 80183906840906820640659903620 // 80183906840906820640659903620 * 10^26 / 79228162514264337593543950336 = // 101206318935480056907421312.890625 if iszero(lt(factor_, 101206318935480056907421313)) { tick := or(tick, 0x8) factor_ := div(mul(factor_, _1E26), 101206318935480056907421313) } // for tick = 4 // ratioX96 = (1.0015^4) * 2^96 = 79704602139525152702959747603 // 79704602139525152702959747603 * 10^26 / 79228162514264337593543950336 = // 100601351350506250000000000 if iszero(lt(factor_, 100601351350506250000000000)) { tick := or(tick, 0x4) factor_ := div(mul(factor_, _1E26), 100601351350506250000000000) } // for tick = 2 // ratioX96 = (1.0015^2) * 2^96 = 79466025265172787701084167660 // 79466025265172787701084167660 * 10^26 / 79228162514264337593543950336 = // 100300225000000000000000000 if iszero(lt(factor_, 100300225000000000000000000)) { tick := or(tick, 0x2) factor_ := div(mul(factor_, _1E26), 100300225000000000000000000) } // for tick = 1 // ratioX96 = (1.0015^1) * 2^96 = 79347004758035734099934266261 // 79347004758035734099934266261 * 10^26 / 79228162514264337593543950336 = // 100150000000000000000000000 if iszero(lt(factor_, 100150000000000000000000000)) { tick := or(tick, 0x1) factor_ := div(mul(factor_, _1E26), 100150000000000000000000000) } if iszero(cond) { // if ratioX96 >= ZERO_TICK_SCALED_RATIO perfectRatioX96 := div(mul(ratioX96, _1E26), factor_) } if cond { // ratioX96 < ZERO_TICK_SCALED_RATIO tick := not(tick) perfectRatioX96 := div(mul(ratioX96, factor_), 100150000000000000000000000) } // perfect ratio should always be <= ratioX96 // not sure if it can ever be bigger but better to have extra checks if gt(perfectRatioX96, ratioX96) { revert(0, 0) } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; abstract contract Structs { struct AddressBool { address addr; bool value; } struct AddressUint256 { address addr; uint256 value; } /// @notice struct to set borrow rate data for version 1 struct RateDataV1Params { /// /// @param token for rate data address token; /// /// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100 /// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast uint256 kink; /// /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100 /// i.e. constant minimum borrow rate /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then) uint256 rateAtUtilizationZero; /// /// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100 /// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700 uint256 rateAtUtilizationKink; /// /// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100 /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500 uint256 rateAtUtilizationMax; } /// @notice struct to set borrow rate data for version 2 struct RateDataV2Params { /// /// @param token for rate data address token; /// /// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100 /// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster uint256 kink1; /// /// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100 /// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast uint256 kink2; /// /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100 /// i.e. constant minimum borrow rate /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then) uint256 rateAtUtilizationZero; /// /// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100 /// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700 uint256 rateAtUtilizationKink1; /// /// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100 /// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200 uint256 rateAtUtilizationKink2; /// /// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100 /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500 uint256 rateAtUtilizationMax; } /// @notice struct to set token config struct TokenConfig { /// /// @param token address address token; /// /// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100 uint256 fee; /// /// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100 uint256 threshold; /// /// @param maxUtilization maximum allowed utilization. in 1e2: 100% = 10_000; 1% = 100 /// set to 100% to disable and have default limit of 100% (avoiding SLOAD). uint256 maxUtilization; } /// @notice struct to set user supply & withdrawal config struct UserSupplyConfig { /// /// @param user address address user; /// /// @param token address address token; /// /// @param mode: 0 = without interest. 1 = with interest uint8 mode; /// /// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100 /// Also used to calculate rate at which withdrawal limit should decrease (instant). uint256 expandPercent; /// /// @param expandDuration withdrawal limit expand duration in seconds. /// used to calculate rate together with expandPercent uint256 expandDuration; /// /// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount. /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token: /// with interest -> raw, without interest -> normal uint256 baseWithdrawalLimit; } /// @notice struct to set user borrow & payback config struct UserBorrowConfig { /// /// @param user address address user; /// /// @param token address address token; /// /// @param mode: 0 = without interest. 1 = with interest uint8 mode; /// /// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100 /// Also used to calculate rate at which debt limit should decrease (instant). uint256 expandPercent; /// /// @param expandDuration debt limit expand duration in seconds. /// used to calculate rate together with expandPercent uint256 expandDuration; /// /// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling /// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place. /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token: /// with interest -> raw, without interest -> normal uint256 baseDebtCeiling; /// /// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow. /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token: /// with interest -> raw, without interest -> normal uint256 maxDebtCeiling; } }
//SPDX-License-Identifier: MIT pragma solidity 0.8.21; import { IProxy } from "../../infiniteProxy/interfaces/iProxy.sol"; import { Structs as AdminModuleStructs } from "../adminModule/structs.sol"; interface IFluidLiquidityAdmin { /// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract. /// auths can be helpful in reducing governance overhead where it's not needed. /// @param authsStatus_ array of structs setting allowed status for an address. /// status true => add auth, false => remove auth function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external; /// @notice adds/removes guardians. Only callable by Governance. /// @param guardiansStatus_ array of structs setting allowed status for an address. /// status true => add guardian, false => remove guardian function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external; /// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance. /// @param revenueCollector_ new revenue collector address function updateRevenueCollector(address revenueCollector_) external; /// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths. /// @param newStatus_ new status /// status = 2 -> pause, status = 1 -> resume. function changeStatus(uint256 newStatus_) external; /// @notice update tokens rate data version 1. Only callable by Auths. /// @param tokensRateData_ array of RateDataV1Params with rate data to set for each token function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external; /// @notice update tokens rate data version 2. Only callable by Auths. /// @param tokensRateData_ array of RateDataV2Params with rate data to set for each token function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external; /// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold. /// Only callable by Auths. /// @param tokenConfigs_ contains token address, fee & utilization threshold function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external; /// @notice updates user classes: 0 is for new protocols, 1 is for established protocols. /// Only callable by Auths. /// @param userClasses_ struct array of uint256 value to assign for each user address function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external; /// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits. /// Only callable by Auths. /// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external; /// @notice sets a new withdrawal limit as the current limit for a certain user /// @param user_ user address for which to update the withdrawal limit /// @param token_ token address for which to update the withdrawal limit /// @param newLimit_ new limit until which user supply can decrease to. /// Important: input in raw. Must account for exchange price in input param calculation. /// Note any limit that is < max expansion or > current user supply will set max expansion limit or /// current user supply as limit respectively. /// - set 0 to make maximum possible withdrawable: instant full expansion, and if that goes /// below base limit then fully down to 0. /// - set type(uint256).max to make current withdrawable 0 (sets current user supply as limit). function updateUserWithdrawalLimit(address user_, address token_, uint256 newLimit_) external; /// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits. /// Only callable by Auths. /// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external; /// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians). /// Only callable by Guardians. /// @param user_ address of user to pause operations for /// @param supplyTokens_ token addresses to pause withdrawals for /// @param borrowTokens_ token addresses to pause borrowings for function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external; /// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians). /// Only callable by Guardians. /// @param user_ address of user to unpause operations for /// @param supplyTokens_ token addresses to unpause withdrawals for /// @param borrowTokens_ token addresses to unpause borrowings for function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external; /// @notice collects revenue for tokens to configured revenueCollector address. /// @param tokens_ array of tokens to collect revenue for /// @dev Note that this can revert if token balance is < revenueAmount (utilization > 100%) function collectRevenue(address[] calldata tokens_) external; /// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage. /// @param tokens_ tokens to update exchange prices for /// @return supplyExchangePrices_ new supply rates of overall system for each token /// @return borrowExchangePrices_ new borrow rates of overall system for each token function updateExchangePrices( address[] calldata tokens_ ) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_); } interface IFluidLiquidityLogic is IFluidLiquidityAdmin { /// @notice Single function which handles supply, withdraw, borrow & payback /// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native) /// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing /// @param borrowAmount_ if +ve then borrow, if -ve then payback, if 0 then nothing /// @param withdrawTo_ if withdrawal then to which address /// @param borrowTo_ if borrow then to which address /// @param callbackData_ callback data passed to `liquidityCallback` method of protocol /// @return memVar3_ updated supplyExchangePrice /// @return memVar4_ updated borrowExchangePrice /// @dev to trigger skipping in / out transfers (gas optimization): /// - ` callbackData_` MUST be encoded so that "from" address is the last 20 bytes in the last 32 bytes slot, /// also for native token operations where liquidityCallback is not triggered! /// from address must come at last position if there is more data. I.e. encode like: /// abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end /// so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end. /// - this "from" address must match withdrawTo_ or borrowTo_ and must be == `msg.sender` /// - `callbackData_` must in addition to the from address as described above include bytes32 SKIP_TRANSFERS /// in the slot before (bytes 32 to 63) /// - `msg.value` must be 0. /// - Amounts must be either: /// - supply(+) == borrow(+), withdraw(-) == payback(-). /// - Liquidity must be on the winning side (deposit < borrow OR payback < withdraw). function operate( address token_, int256 supplyAmount_, int256 borrowAmount_, address withdrawTo_, address borrowTo_, bytes calldata callbackData_ ) external payable returns (uint256 memVar3_, uint256 memVar4_); } interface IFluidLiquidity is IProxy, IFluidLiquidityLogic {}
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; contract Error { error FluidOracleError(uint256 errorId_); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; library ErrorTypes { /***********************************| | FluidOracleL2 | |__________________________________*/ /// @notice thrown when sequencer on a L2 has an outage and grace period has not yet passed. uint256 internal constant FluidOracleL2__SequencerOutage = 60000; /***********************************| | UniV3CheckCLRSOracle | |__________________________________*/ /// @notice thrown when the delta between main price source and check rate source is exceeding the allowed delta uint256 internal constant UniV3CheckCLRSOracle__InvalidPrice = 60001; /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant UniV3CheckCLRSOracle__InvalidParams = 60002; /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config uint256 internal constant UniV3CheckCLRSOracle__ExchangeRateZero = 60003; /***********************************| | FluidOracle | |__________________________________*/ /// @notice thrown when an invalid info name is passed into a fluid oracle (e.g. not set or too long) uint256 internal constant FluidOracle__InvalidInfoName = 60010; /***********************************| | sUSDe Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant SUSDeOracle__InvalidParams = 60102; /***********************************| | Pendle Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant PendleOracle__InvalidParams = 60201; /// @notice thrown when the Pendle market Oracle has not been initialized yet uint256 internal constant PendleOracle__MarketNotInitialized = 60202; /// @notice thrown when the Pendle market does not have 18 decimals uint256 internal constant PendleOracle__MarketInvalidDecimals = 60203; /// @notice thrown when the Pendle market returns an unexpected price uint256 internal constant PendleOracle__InvalidPrice = 60204; /***********************************| | CLRS2UniV3CheckCLRSOracleL2 | |__________________________________*/ /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config uint256 internal constant CLRS2UniV3CheckCLRSOracleL2__ExchangeRateZero = 60301; /***********************************| | Ratio2xFallbackCLRSOracleL2 | |__________________________________*/ /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config uint256 internal constant Ratio2xFallbackCLRSOracleL2__ExchangeRateZero = 60311; /***********************************| | WeETHsOracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant WeETHsOracle__InvalidParams = 60321; /***********************************| | DexSmartColOracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant DexSmartColOracle__InvalidParams = 60331; /// @notice thrown when smart col is not enabled uint256 internal constant DexSmartColOracle__SmartColNotEnabled = 60332; /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config uint256 internal constant DexSmartColOracle__ExchangeRateZero = 60333; /***********************************| | DexSmartDebtOracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant DexSmartDebtOracle__InvalidParams = 60341; /// @notice thrown when smart debt is not enabled uint256 internal constant DexSmartDebtOracle__SmartDebtNotEnabled = 60342; /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config uint256 internal constant DexSmartDebtOracle__ExchangeRateZero = 60343; /***********************************| | ContractRate | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant ContractRate__InvalidParams = 60351; /// @notice thrown when caller is not authorized uint256 internal constant ContractRate__Unauthorized = 60352; /// @notice thrown when minimum diff for triggering update on the stared rate is not reached uint256 internal constant ContractRate__MinUpdateDiffNotReached = 60353; /// @notice thrown when the external rate source returns 0 for the new rate uint256 internal constant ContractRate__NewRateZero = 60354; /***********************************| | sUSDs Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant SUSDsOracle__InvalidParams = 60361; /***********************************| | Peg Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant PegOracle__InvalidParams = 60371; /***********************************| | DexOracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant DexOracle__InvalidParams = 60381; /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config uint256 internal constant DexOracle__ExchangeRateZero = 60382; /***********************************| | GenericOracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant GenericOracle__InvalidParams = 60401; /// @notice thrown when reaching an unexepcted config state uint256 internal constant GenericOracle__UnexpectedConfig = 60402; /// @notice thrown when the exchange rate is zero uint256 internal constant GenericOracle__RateZero = 60403; /***********************************| | Chainlink Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant ChainlinkOracle__InvalidParams = 61001; /***********************************| | UniswapV3 Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant UniV3Oracle__InvalidParams = 62001; /// @notice thrown when constructor is called with invalid ordered seconds agos values uint256 internal constant UniV3Oracle__InvalidSecondsAgos = 62002; /// @notice thrown when constructor is called with invalid delta values > 100% uint256 internal constant UniV3Oracle__InvalidDeltas = 62003; /***********************************| | WstETh Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant WstETHOracle__InvalidParams = 63001; /***********************************| | Redstone Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant RedstoneOracle__InvalidParams = 64001; /***********************************| | Fallback Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant FallbackOracle__InvalidParams = 65001; /***********************************| | FallbackCLRSOracle | |__________________________________*/ /// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled) uint256 internal constant FallbackCLRSOracle__ExchangeRateZero = 66001; /***********************************| | WstETHCLRSOracle | |__________________________________*/ /// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled) uint256 internal constant WstETHCLRSOracle__ExchangeRateZero = 67001; /***********************************| | CLFallbackUniV3Oracle | |__________________________________*/ /// @notice thrown when the exchange rate is zero, even for the uniV3 rate uint256 internal constant CLFallbackUniV3Oracle__ExchangeRateZero = 68001; /***********************************| | WstETHCLRS2UniV3CheckCLRSOracle | |__________________________________*/ /// @notice thrown when the exchange rate is zero, even for the uniV3 rate uint256 internal constant WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero = 69001; /***********************************| | WeETh Oracle | |__________________________________*/ /// @notice thrown when an invalid parameter is passed to a method uint256 internal constant WeETHOracle__InvalidParams = 70001; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { IFluidOracle } from "./interfaces/iFluidOracle.sol"; import { ErrorTypes } from "./errorTypes.sol"; import { Error as OracleError } from "./error.sol"; /// @title FluidOracle /// @notice Base contract that any Fluid Oracle must implement abstract contract FluidOracle is IFluidOracle, OracleError { /// @dev short helper string to easily identify the oracle. E.g. token symbols // // using a bytes32 because string can not be immutable. bytes32 private immutable _infoName; constructor(string memory infoName_) { if (bytes(infoName_).length > 32 || bytes(infoName_).length == 0) { revert FluidOracleError(ErrorTypes.FluidOracle__InvalidInfoName); } // convert string to bytes32 bytes32 infoNameBytes32_; assembly { infoNameBytes32_ := mload(add(infoName_, 32)) } _infoName = infoNameBytes32_; } /// @inheritdoc IFluidOracle function infoName() external view returns (string memory) { // convert bytes32 to string uint256 length_; while (length_ < 32 && _infoName[length_] != 0) { length_++; } bytes memory infoNameBytes_ = new bytes(length_); for (uint256 i; i < length_; i++) { infoNameBytes_[i] = _infoName[i]; } return string(infoNameBytes_); } /// @inheritdoc IFluidOracle function getExchangeRate() external view virtual returns (uint256 exchangeRate_); /// @inheritdoc IFluidOracle function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_); /// @inheritdoc IFluidOracle function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IFluidOracle { /// @dev Deprecated. Use `getExchangeRateOperate()` and `getExchangeRateLiquidate()` instead. Only implemented for /// backwards compatibility. function getExchangeRate() external view returns (uint256 exchangeRate_); /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for operates function getExchangeRateOperate() external view returns (uint256 exchangeRate_); /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for liquidations function getExchangeRateLiquidate() external view returns (uint256 exchangeRate_); /// @notice helper string to easily identify the oracle. E.g. token symbols function infoName() external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IFluidDexT1 { error FluidDexError(uint256 errorId); /// @notice used to simulate swap to find the output amount error FluidDexSwapResult(uint256 amountOut); error FluidDexPerfectLiquidityOutput(uint256 token0Amt, uint token1Amt); error FluidDexSingleTokenOutput(uint256 tokenAmt); error FluidDexLiquidityOutput(uint256 shares); error FluidDexPricesAndExchangeRates(PricesAndExchangePrice pex_); /// @notice returns the dex id function DEX_ID() external view returns (uint256); /// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key. function readFromStorage(bytes32 slot_) external view returns (uint256 result_); struct Implementations { address shift; address admin; address colOperations; address debtOperations; address perfectOperationsAndOracle; } struct ConstantViews { uint256 dexId; address liquidity; address factory; Implementations implementations; address deployerContract; address token0; address token1; bytes32 supplyToken0Slot; bytes32 borrowToken0Slot; bytes32 supplyToken1Slot; bytes32 borrowToken1Slot; bytes32 exchangePriceToken0Slot; bytes32 exchangePriceToken1Slot; uint256 oracleMapping; } struct ConstantViews2 { uint token0NumeratorPrecision; uint token0DenominatorPrecision; uint token1NumeratorPrecision; uint token1DenominatorPrecision; } struct PricesAndExchangePrice { uint lastStoredPrice; // last stored price in 1e27 decimals uint centerPrice; // last stored price in 1e27 decimals uint upperRange; // price at upper range in 1e27 decimals uint lowerRange; // price at lower range in 1e27 decimals uint geometricMean; // geometric mean of upper range & lower range in 1e27 decimals uint supplyToken0ExchangePrice; uint borrowToken0ExchangePrice; uint supplyToken1ExchangePrice; uint borrowToken1ExchangePrice; } struct CollateralReserves { uint token0RealReserves; uint token1RealReserves; uint token0ImaginaryReserves; uint token1ImaginaryReserves; } struct DebtReserves { uint token0Debt; uint token1Debt; uint token0RealReserves; uint token1RealReserves; uint token0ImaginaryReserves; uint token1ImaginaryReserves; } function getCollateralReserves( uint geometricMean_, uint upperRange_, uint lowerRange_, uint token0SupplyExchangePrice_, uint token1SupplyExchangePrice_ ) external view returns (CollateralReserves memory c_); function getDebtReserves( uint geometricMean_, uint upperRange_, uint lowerRange_, uint token0BorrowExchangePrice_, uint token1BorrowExchangePrice_ ) external view returns (DebtReserves memory d_); // reverts with FluidDexPricesAndExchangeRates(pex_); function getPricesAndExchangePrices() external; function constantsView() external view returns (ConstantViews memory constantsView_); function constantsView2() external view returns (ConstantViews2 memory constantsView2_); struct Oracle { uint twap1by0; // TWAP price uint lowestPrice1by0; // lowest price point uint highestPrice1by0; // highest price point uint twap0by1; // TWAP price uint lowestPrice0by1; // lowest price point uint highestPrice0by1; // highest price point } /// @dev This function allows users to swap a specific amount of input tokens for output tokens /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0 /// @param amountIn_ The exact amount of input tokens to swap /// @param amountOutMin_ The minimum amount of output tokens the user is willing to accept /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountOut_ /// @return amountOut_ The amount of output tokens received from the swap function swapIn( bool swap0to1_, uint256 amountIn_, uint256 amountOutMin_, address to_ ) external payable returns (uint256 amountOut_); /// @dev Swap tokens with perfect amount out /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0 /// @param amountOut_ The exact amount of tokens to receive after swap /// @param amountInMax_ Maximum amount of tokens to swap in /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountIn_ /// @return amountIn_ The amount of input tokens used for the swap function swapOut( bool swap0to1_, uint256 amountOut_, uint256 amountInMax_, address to_ ) external payable returns (uint256 amountIn_); /// @dev Deposit tokens in equal proportion to the current pool ratio /// @param shares_ The number of shares to mint /// @param maxToken0Deposit_ Maximum amount of token0 to deposit /// @param maxToken1Deposit_ Maximum amount of token1 to deposit /// @param estimate_ If true, function will revert with estimated deposit amounts without executing the deposit /// @return token0Amt_ Amount of token0 deposited /// @return token1Amt_ Amount of token1 deposited function depositPerfect( uint shares_, uint maxToken0Deposit_, uint maxToken1Deposit_, bool estimate_ ) external payable returns (uint token0Amt_, uint token1Amt_); /// @dev This function allows users to withdraw a perfect amount of collateral liquidity /// @param shares_ The number of shares to withdraw /// @param minToken0Withdraw_ The minimum amount of token0 the user is willing to accept /// @param minToken1Withdraw_ The minimum amount of token1 the user is willing to accept /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_ /// @return token0Amt_ The amount of token0 withdrawn /// @return token1Amt_ The amount of token1 withdrawn function withdrawPerfect( uint shares_, uint minToken0Withdraw_, uint minToken1Withdraw_, address to_ ) external returns (uint token0Amt_, uint token1Amt_); /// @dev This function allows users to borrow tokens in equal proportion to the current debt pool ratio /// @param shares_ The number of shares to borrow /// @param minToken0Borrow_ Minimum amount of token0 to borrow /// @param minToken1Borrow_ Minimum amount of token1 to borrow /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_ /// @return token0Amt_ Amount of token0 borrowed /// @return token1Amt_ Amount of token1 borrowed function borrowPerfect( uint shares_, uint minToken0Borrow_, uint minToken1Borrow_, address to_ ) external returns (uint token0Amt_, uint token1Amt_); /// @dev This function allows users to pay back borrowed tokens in equal proportion to the current debt pool ratio /// @param shares_ The number of shares to pay back /// @param maxToken0Payback_ Maximum amount of token0 to pay back /// @param maxToken1Payback_ Maximum amount of token1 to pay back /// @param estimate_ If true, function will revert with estimated payback amounts without executing the payback /// @return token0Amt_ Amount of token0 paid back /// @return token1Amt_ Amount of token1 paid back function paybackPerfect( uint shares_, uint maxToken0Payback_, uint maxToken1Payback_, bool estimate_ ) external payable returns (uint token0Amt_, uint token1Amt_); /// @dev This function allows users to deposit tokens in any proportion into the col pool /// @param token0Amt_ The amount of token0 to deposit /// @param token1Amt_ The amount of token1 to deposit /// @param minSharesAmt_ The minimum amount of shares the user expects to receive /// @param estimate_ If true, function will revert with estimated shares without executing the deposit /// @return shares_ The amount of shares minted for the deposit function deposit( uint token0Amt_, uint token1Amt_, uint minSharesAmt_, bool estimate_ ) external payable returns (uint shares_); /// @dev This function allows users to withdraw tokens in any proportion from the col pool /// @param token0Amt_ The amount of token0 to withdraw /// @param token1Amt_ The amount of token1 to withdraw /// @param maxSharesAmt_ The maximum number of shares the user is willing to burn /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_ /// @return shares_ The number of shares burned for the withdrawal function withdraw( uint token0Amt_, uint token1Amt_, uint maxSharesAmt_, address to_ ) external returns (uint shares_); /// @dev This function allows users to borrow tokens in any proportion from the debt pool /// @param token0Amt_ The amount of token0 to borrow /// @param token1Amt_ The amount of token1 to borrow /// @param maxSharesAmt_ The maximum amount of shares the user is willing to receive /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_ /// @return shares_ The amount of borrow shares minted to represent the borrowed amount function borrow( uint token0Amt_, uint token1Amt_, uint maxSharesAmt_, address to_ ) external returns (uint shares_); /// @dev This function allows users to payback tokens in any proportion to the debt pool /// @param token0Amt_ The amount of token0 to payback /// @param token1Amt_ The amount of token1 to payback /// @param minSharesAmt_ The minimum amount of shares the user expects to burn /// @param estimate_ If true, function will revert with estimated shares without executing the payback /// @return shares_ The amount of borrow shares burned for the payback function payback( uint token0Amt_, uint token1Amt_, uint minSharesAmt_, bool estimate_ ) external payable returns (uint shares_); /// @dev This function allows users to withdraw their collateral with perfect shares in one token /// @param shares_ The number of shares to burn for withdrawal /// @param minToken0_ The minimum amount of token0 the user expects to receive (set to 0 if withdrawing in token1) /// @param minToken1_ The minimum amount of token1 the user expects to receive (set to 0 if withdrawing in token0) /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with withdrawAmt_ /// @return withdrawAmt_ The amount of tokens withdrawn in the chosen token function withdrawPerfectInOneToken( uint shares_, uint minToken0_, uint minToken1_, address to_ ) external returns ( uint withdrawAmt_ ); /// @dev This function allows users to payback their debt with perfect shares in one token /// @param shares_ The number of shares to burn for payback /// @param maxToken0_ The maximum amount of token0 the user is willing to pay (set to 0 if paying back in token1) /// @param maxToken1_ The maximum amount of token1 the user is willing to pay (set to 0 if paying back in token0) /// @param estimate_ If true, the function will revert with the estimated payback amount without executing the payback /// @return paybackAmt_ The amount of tokens paid back in the chosen token function paybackPerfectInOneToken( uint shares_, uint maxToken0_, uint maxToken1_, bool estimate_ ) external payable returns ( uint paybackAmt_ ); /// @dev the oracle assumes last set price of pool till the next swap happens. /// There's a possibility that during that time some interest is generated hence the last stored price is not the 100% correct price for the whole duration /// but the difference due to interest will be super low so this difference is ignored /// For example 2 swaps happened 10min (600 seconds) apart and 1 token has 10% higher interest than other. /// then that token will accrue about 10% * 600 / secondsInAYear = ~0.0002% /// @param secondsAgos_ array of seconds ago for which TWAP is needed. If user sends [10, 30, 60] then twaps_ will return [10-0, 30-10, 60-30] /// @return twaps_ twap price, lowest price (aka minima) & highest price (aka maxima) between secondsAgo checkpoints /// @return currentPrice_ price of pool after the most recent swap function oraclePrice( uint[] memory secondsAgos_ ) external view returns ( Oracle[] memory twaps_, uint currentPrice_ ); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; abstract contract Error { error FluidVaultError(uint256 errorId_); /// @notice used to simulate liquidation to find the maximum liquidatable amounts error FluidLiquidateResult(uint256 colLiquidated, uint256 debtLiquidated); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; library ErrorTypes { /***********************************| | Vault Factory | |__________________________________*/ uint256 internal constant VaultFactory__InvalidOperation = 30001; uint256 internal constant VaultFactory__Unauthorized = 30002; uint256 internal constant VaultFactory__SameTokenNotAllowed = 30003; uint256 internal constant VaultFactory__InvalidParams = 30004; uint256 internal constant VaultFactory__InvalidVault = 30005; uint256 internal constant VaultFactory__InvalidVaultAddress = 30006; uint256 internal constant VaultFactory__OnlyDelegateCallAllowed = 30007; /***********************************| | Vault | |__________________________________*/ /// @notice thrown at reentrancy uint256 internal constant Vault__AlreadyEntered = 31001; /// @notice thrown when user sends deposit & borrow amount as 0 uint256 internal constant Vault__InvalidOperateAmount = 31002; /// @notice thrown when msg.value is not in sync with native token deposit or payback uint256 internal constant Vault__InvalidMsgValueOperate = 31003; /// @notice thrown when msg.sender is not the owner of the vault uint256 internal constant Vault__NotAnOwner = 31004; /// @notice thrown when user's position does not exist. Sending the wrong index from the frontend uint256 internal constant Vault__TickIsEmpty = 31005; /// @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 uint256 internal constant Vault__PositionAboveCF = 31006; /// @notice thrown when the top tick is not initialized. Happens if the vault is totally new or all the user's left uint256 internal constant Vault__TopTickDoesNotExist = 31007; /// @notice thrown when msg.value in liquidate is not in sync payback uint256 internal constant Vault__InvalidMsgValueLiquidate = 31008; /// @notice thrown when slippage is more on liquidation than what the liquidator sent uint256 internal constant Vault__ExcessSlippageLiquidation = 31009; /// @notice thrown when msg.sender is not the rebalancer/reserve contract uint256 internal constant Vault__NotRebalancer = 31010; /// @notice thrown when NFT of one vault interacts with the NFT of other vault uint256 internal constant Vault__NftNotOfThisVault = 31011; /// @notice thrown when the token is not initialized on the liquidity contract uint256 internal constant Vault__TokenNotInitialized = 31012; /// @notice thrown when admin updates fallback if a non-auth calls vault uint256 internal constant Vault__NotAnAuth = 31013; /// @notice thrown in operate when user tries to witdhraw more collateral than deposited uint256 internal constant Vault__ExcessCollateralWithdrawal = 31014; /// @notice thrown in operate when user tries to payback more debt than borrowed uint256 internal constant Vault__ExcessDebtPayback = 31015; /// @notice thrown when user try to withdrawal more than operate's withdrawal limit uint256 internal constant Vault__WithdrawMoreThanOperateLimit = 31016; /// @notice thrown when caller of liquidityCallback is not Liquidity uint256 internal constant Vault__InvalidLiquidityCallbackAddress = 31017; /// @notice thrown when reentrancy is not already on uint256 internal constant Vault__NotEntered = 31018; /// @notice thrown when someone directly calls operate or secondary implementation contract uint256 internal constant Vault__OnlyDelegateCallAllowed = 31019; /// @notice thrown when the safeTransferFrom for a token amount failed uint256 internal constant Vault__TransferFromFailed = 31020; /// @notice thrown when exchange price overflows while updating on storage uint256 internal constant Vault__ExchangePriceOverFlow = 31021; /// @notice thrown when debt to liquidate amt is sent wrong uint256 internal constant Vault__InvalidLiquidationAmt = 31022; /// @notice thrown when user debt or collateral goes above 2**128 or below -2**128 uint256 internal constant Vault__UserCollateralDebtExceed = 31023; /// @notice thrown if on liquidation branch debt becomes lower than 100 uint256 internal constant Vault__BranchDebtTooLow = 31024; /// @notice thrown when tick's debt is less than 10000 uint256 internal constant Vault__TickDebtTooLow = 31025; /// @notice thrown when the received new liquidity exchange price is of unexpected value (< than the old one) uint256 internal constant Vault__LiquidityExchangePriceUnexpected = 31026; /// @notice thrown when user's debt is less than 10000 uint256 internal constant Vault__UserDebtTooLow = 31027; /// @notice thrown when on only payback and only deposit the ratio of position increases uint256 internal constant Vault__InvalidPaybackOrDeposit = 31028; /// @notice thrown when liquidation just happens of a single partial or when there's nothing to liquidate uint256 internal constant Vault__InvalidLiquidation = 31029; /// @notice thrown when msg.value is sent wrong in rebalance uint256 internal constant Vault__InvalidMsgValueInRebalance = 31030; /// @notice thrown when nothing rebalanced uint256 internal constant Vault__NothingToRebalance = 31031; /// @notice thrown on unforseen liquidation scenarios. Might never come in use. uint256 internal constant Vault__LiquidationReverts = 31032; /// @notice thrown when oracle price is > 1e54 uint256 internal constant Vault__InvalidOraclePrice = 31033; /// @notice thrown when constants are not set properly via contructor uint256 internal constant Vault__ImproperConstantsSetup = 31034; /// @notice thrown when externally calling fetchLatestPosition function uint256 internal constant Vault__FetchLatestPositionFailed = 31035; /// @notice thrown when dex callback is not from dex uint256 internal constant Vault__InvalidDexCallbackAddress = 31036; /// @notice thrown when dex callback is already set uint256 internal constant Vault__DexFromAddressAlreadySet = 31037; /// @notice thrown when an invalid min / max amounts config is passed to rebalance() uint256 internal constant Vault__InvalidMinMaxInRebalance = 31038; /***********************************| | ERC721 | |__________________________________*/ uint256 internal constant ERC721__InvalidParams = 32001; uint256 internal constant ERC721__Unauthorized = 32002; uint256 internal constant ERC721__InvalidOperation = 32003; uint256 internal constant ERC721__UnsafeRecipient = 32004; uint256 internal constant ERC721__OutOfBoundsIndex = 32005; /***********************************| | Vault Admin | |__________________________________*/ /// @notice thrown when admin tries to setup invalid value which are crossing limits uint256 internal constant VaultAdmin__ValueAboveLimit = 33001; /// @notice when someone directly calls admin implementation contract uint256 internal constant VaultAdmin__OnlyDelegateCallAllowed = 33002; /// @notice thrown when auth sends NFT ID as 0 while collecting dust debt uint256 internal constant VaultAdmin__NftIdShouldBeNonZero = 33003; /// @notice thrown when trying to collect dust debt of NFT which is not of this vault uint256 internal constant VaultAdmin__NftNotOfThisVault = 33004; /// @notice thrown when dust debt of NFT is 0, meaning nothing to collect uint256 internal constant VaultAdmin__DustDebtIsZero = 33005; /// @notice thrown when final debt after liquidation is not 0, meaning position 100% liquidated uint256 internal constant VaultAdmin__FinalDebtShouldBeZero = 33006; /// @notice thrown when NFT is not liquidated state uint256 internal constant VaultAdmin__NftNotLiquidated = 33007; /// @notice thrown when total absorbed dust debt is 0 uint256 internal constant VaultAdmin__AbsorbedDustDebtIsZero = 33008; /// @notice thrown when address is set as 0 uint256 internal constant VaultAdmin__AddressZeroNotAllowed = 33009; /***********************************| | Vault Rewards | |__________________________________*/ uint256 internal constant VaultRewards__Unauthorized = 34001; uint256 internal constant VaultRewards__AddressZero = 34002; uint256 internal constant VaultRewards__InvalidParams = 34003; uint256 internal constant VaultRewards__NewMagnifierSameAsOldMagnifier = 34004; uint256 internal constant VaultRewards__NotTheInitiator = 34005; uint256 internal constant VaultRewards__NotTheGovernance = 34006; uint256 internal constant VaultRewards__AlreadyStarted = 34007; uint256 internal constant VaultRewards__RewardsNotStartedOrEnded = 34008; uint256 internal constant VaultRewards__InvalidStartTime = 34009; uint256 internal constant VaultRewards__AlreadyEnded = 34010; /***********************************| | Vault DEX Types | |__________________________________*/ uint256 internal constant VaultDex__InvalidOperateAmount = 35001; uint256 internal constant VaultDex__DebtSharesPaidMoreThanAvailableLiquidation = 35002; /***********************************| | Vault Borrow Rewards | |__________________________________*/ uint256 internal constant VaultBorrowRewards__Unauthorized = 36001; uint256 internal constant VaultBorrowRewards__AddressZero = 36002; uint256 internal constant VaultBorrowRewards__InvalidParams = 36003; uint256 internal constant VaultBorrowRewards__NewMagnifierSameAsOldMagnifier = 36004; uint256 internal constant VaultBorrowRewards__NotTheInitiator = 36005; uint256 internal constant VaultBorrowRewards__NotTheGovernance = 36006; uint256 internal constant VaultBorrowRewards__AlreadyStarted = 36007; uint256 internal constant VaultBorrowRewards__RewardsNotStartedOrEnded = 36008; uint256 internal constant VaultBorrowRewards__InvalidStartTime = 36009; uint256 internal constant VaultBorrowRewards__AlreadyEnded = 36010; }
//SPDX-License-Identifier: MIT pragma solidity 0.8.21; import { IFluidLiquidityLogic } from "../../../liquidity/interfaces/iLiquidity.sol"; import { IFluidDexT1 } from "../../dex/interfaces/iDexT1.sol"; interface ILiquidityDexCommon is IFluidLiquidityLogic, IFluidDexT1 { /// @notice only importing IFluidLiquidityLogic as readFromStorage is also defined in iDexT1 as well so to avoid clashing }
//SPDX-License-Identifier: MIT pragma solidity 0.8.21; import { IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; interface IFluidVaultFactory is IERC721Enumerable { /// @notice Minting an NFT Vault for the user function mint(uint256 vaultId_, address user_) external returns (uint256 tokenId_); /// @notice returns owner of Vault which is also an NFT function ownerOf(uint256 tokenId) external view returns (address owner); /// @notice Global auth is auth for all vaults function isGlobalAuth(address auth_) external view returns (bool); /// @notice Vault auth is auth for a specific vault function isVaultAuth(address vault_, address auth_) external view returns (bool); /// @notice Total vaults deployed. function totalVaults() external view returns (uint256); /// @notice Compute vaultAddress function getVaultAddress(uint256 vaultId) external view returns (address); /// @notice read uint256 `result_` for a storage `slot_` key function readFromStorage(bytes32 slot_) external view returns (uint256 result_); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { SafeTransfer } from "../../../../libraries/safeTransfer.sol"; import { ErrorTypes } from "../../errorTypes.sol"; import { Error } from "../../error.sol"; abstract contract TokenTransfers is Error { function _validateEth(uint initialEth_) internal { uint finalEth_ = payable(address(this)).balance; if (finalEth_ > initialEth_) { unchecked { SafeTransfer.safeTransferNative(msg.sender, finalEth_ - initialEth_); // sending back excess ETH } } else if (finalEth_ < initialEth_) { revert FluidVaultError(ErrorTypes.Vault__InvalidMsgValueOperate); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; abstract contract Variables { /***********************************| | Storage Variables | |__________________________________*/ /// note: in all variables. For tick >= 0 are represented with bit as 1, tick < 0 are represented with bit as 0 /// note: read all the variables through storageRead.sol /// note: vaultVariables contains vault variables which need regular updates through transactions /// First 1 bit => 0 => re-entrancy. If 0 then allow transaction to go, else throw. /// Next 1 bit => 1 => Is the current active branch liquidated? If true then check the branch's minima tick before creating a new position /// If the new tick is greater than minima tick then initialize a new branch, make that as current branch & do proper linking /// Next 1 bit => 2 => sign of topmost tick (0 -> negative; 1 -> positive) /// Next 19 bits => 3-21 => absolute value of topmost tick /// Next 30 bits => 22-51 => current branch ID /// Next 30 bits => 52-81 => total branch ID /// Next 64 bits => 82-145 => Total supply /// Next 64 bits => 146-209 => Total borrow /// Next 32 bits => 210-241 => Total positions uint256 internal vaultVariables; /// note: vaultVariables2 contains variables which do not update on every transaction. So mainly admin/auth set amount /// First 16 bits => 0-15 => supply rate magnifier; 10000 = 1x (Here 16 bits should be more than enough) /// Next 16 bits => 16-31 => borrow rate magnifier; 10000 = 1x (Here 16 bits should be more than enough) /// Next 10 bits => 32-41 => collateral factor. 800 = 0.8 = 80% (max precision of 0.1%) /// Next 10 bits => 42-51 => liquidation Threshold. 900 = 0.9 = 90% (max precision of 0.1%) /// Next 10 bits => 52-61 => liquidation Max Limit. 950 = 0.95 = 95% (max precision of 0.1%) (above this 100% liquidation can happen) /// 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. /// 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. /// 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. /// Next 30 bits => 92-121 => bits to calculate address of oracle /// Next 33 bits => 122-154 => last update timestamp uint256 internal vaultVariables2; /// note: stores absorbed liquidity /// First 128 bits raw debt amount /// last 128 bits raw col amount uint256 internal absorbedLiquidity; /// position index => position data uint /// if the entire variable is 0 (meaning not initialized) at the start that means no position at all /// First 1 bit => 0 => position type (0 => borrow position; 1 => supply position) /// Next 1 bit => 1 => sign of user's tick (0 => negative; 1 => positive) /// Next 19 bits => 2-20 => absolute value of user's tick /// Next 24 bits => 21-44 => user's tick's id /// 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 /// Next 64 bits => 45-108 => user's supply amount. Debt will be calculated through supply & ratio. /// 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 /// 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. mapping(uint256 => uint256) internal positionData; /// Tick has debt only keeps data of non liquidated positions. liquidated tick's data stays in branch itself /// tick parent => uint (represents bool for 256 children) /// parent of (i)th tick:- /// if (i>=0) (i / 256); /// else ((i + 1) / 256) - 1 /// first bit of the variable is the smallest tick & last bit is the biggest tick of that slot mapping(int256 => uint256) internal tickHasDebt; /// mapping tickId => tickData /// Tick related data. Total debt & other things /// First bit => 0 => If 1 then liquidated else not liquidated /// Next 24 bits => 1-24 => Total IDs. ID should start from 1. /// If not liquidated: /// Next 64 bits => 25-88 => raw debt /// If liquidated /// The below 3 things are of last ID. This is to be updated when user creates a new position /// Next 1 bit => 25 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated (100% liquidated) /// Next 30 bits => 26-55 => branch ID where this tick got liquidated /// Next 50 bits => 56-105 => debt factor 50 bits (35 bits coefficient | 15 bits expansion) mapping(int256 => uint256) internal tickData; /// tick id => previous tick id liquidation data. ID starts from 1 /// One tick ID contains 3 IDs of 80 bits in it, holding liquidation data of previously active but liquidated ticks /// 81 bits data below /// #### First 85 bits #### /// 1st bit => 0 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated /// Next 30 bits => 1-30 => branch ID where this tick got liquidated /// Next 50 bits => 31-80 => debt factor 50 bits (35 bits coefficient | 15 bits expansion) /// #### Second 85 bits #### /// 85th bit => 85 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated /// Next 30 bits => 86-115 => branch ID where this tick got liquidated /// Next 50 bits => 116-165 => debt factor 50 bits (35 bits coefficient | 15 bits expansion) /// #### Third 85 bits #### /// 170th bit => 170 => Is 100% liquidated? If this is 1 meaning it was above max tick when it got liquidated /// Next 30 bits => 171-200 => branch ID where this tick got liquidated /// Next 50 bits => 201-250 => debt factor 50 bits (35 bits coefficient | 15 bits expansion) mapping(int256 => mapping(uint256 => uint256)) internal tickId; /// mapping branchId => branchData /// First 2 bits => 0-1 => if 0 then not liquidated, if 1 then liquidated, if 2 then merged, if 3 then closed /// merged means the branch is merged into it's base branch /// closed means all the users are 100% liquidated /// Next 1 bit => 2 => minima tick sign of this branch. Will only be there if any liquidation happened. /// Next 19 bits => 3-21 => minima tick of this branch. Will only be there if any liquidation happened. /// Next 30 bits => 22-51 => Partials of minima tick of branch this is connected to. 0 if master branch. /// 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 /// If not merged /// Next 50 bits => 116-165 => Debt factor or of this branch. (35 bits coefficient | 15 bits expansion) /// If merged /// Next 50 bits => 116-165 => Connection/adjustment debt factor of this branch with the next branch. /// If closed /// Next 50 bits => 116-165 => Debt factor as 0. As all the user's positions are now fully gone /// following values are present always again (merged / not merged / closed) /// Next 30 bits => 166-195 => Branch's ID with which this branch is connected. If 0 then that means this is the master branch /// Next 1 bit => 196 => sign of minima tick of branch this is connected to. 0 if master branch. /// Next 19 bits => 197-215 => minima tick of branch this is connected to. 0 if master branch. mapping(uint256 => uint256) internal branchData; /// Exchange prices are in 1e12 /// First 64 bits => 0-63 => Liquidity's collateral token supply exchange price /// First 64 bits => 64-127 => Liquidity's debt token borrow exchange price /// First 64 bits => 128-191 => Vault's collateral token supply exchange price /// First 64 bits => 192-255 => Vault's debt token borrow exchange price uint256 internal rates; /// address of rebalancer address internal rebalancer; uint256 internal absorbedDustDebt; address internal dexFromAddress; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { IFluidVaultFactory } from "../../interfaces/iVaultFactory.sol"; import { IFluidLiquidity } from "../../../../liquidity/interfaces/iLiquidity.sol"; import { StorageRead } from "../../../../libraries/storageRead.sol"; import { ILiquidityDexCommon } from "../../interfaces/iLiquidityDexCommon.sol"; import { Structs } from "./structs.sol"; import { Error } from "../../error.sol"; import { ErrorTypes } from "../../errorTypes.sol"; import { FluidProtocolTypes } from "../../../../libraries/fluidProtocolTypes.sol"; interface TokenInterface { function decimals() external view returns (uint8); } abstract contract ConstantVariables is StorageRead, Structs, Error { /***********************************| | Constant Variables | |__________________________________*/ address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address internal constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD; /// @dev collateral token address address internal immutable SUPPLY_TOKEN; /// @dev borrow token address address internal immutable BORROW_TOKEN; /// @dev contract via which we deploy oracle contract address internal immutable DEPLOYER_CONTRACT; ILiquidityDexCommon internal immutable SUPPLY; ILiquidityDexCommon internal immutable BORROW; /// @dev if smart collateral then token0 is dex token0 address else it's normal collateral token0 address address internal immutable SUPPLY_TOKEN0; /// @dev if smart collateral then token1 is dex token1 address else it's address(0) address internal immutable SUPPLY_TOKEN1; /// @dev if smart debt then token0 is dex token0 address else it's normal borrow token0 address address internal immutable BORROW_TOKEN0; /// @dev if smart debt then token1 is dex token1 address else it's address(0) address internal immutable BORROW_TOKEN1; /// @dev Vault OperateModule implemenation address address internal immutable OPERATE_IMPLEMENTATION; /// @dev Vault AdminModule implemenation address address internal immutable ADMIN_IMPLEMENTATION; /// @dev Vault Secondary implemenation (main2.sol) address address internal immutable SECONDARY_IMPLEMENTATION; /// @dev liquidity proxy contract address IFluidLiquidity public immutable LIQUIDITY; /// @dev vault factory contract address IFluidVaultFactory public immutable VAULT_FACTORY; uint public immutable VAULT_ID; uint public immutable TYPE; uint internal constant X8 = 0xff; uint internal constant X10 = 0x3ff; uint internal constant X15 = 0x7fff; uint internal constant X16 = 0xffff; uint internal constant X19 = 0x7ffff; uint internal constant X20 = 0xfffff; uint internal constant X24 = 0xffffff; uint internal constant X25 = 0x1ffffff; uint internal constant X30 = 0x3fffffff; uint internal constant X33 = 0x1ffffffff; uint internal constant X35 = 0x7ffffffff; uint internal constant X50 = 0x3ffffffffffff; uint internal constant X64 = 0xffffffffffffffff; uint internal constant X96 = 0xffffffffffffffffffffffff; uint internal constant X128 = 0xffffffffffffffffffffffffffffffff; uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12; /// @dev slot ids in Liquidity contract. Helps in low gas fetch from liquidity contract by skipping delegate call bytes32 internal immutable SUPPLY_EXCHANGE_PRICE_SLOT; // Can be of DEX or liquidity layer bytes32 internal immutable BORROW_EXCHANGE_PRICE_SLOT; // Can be of DEX or liquidity layer bytes32 internal immutable USER_SUPPLY_SLOT; // Can be of DEX or liquidity layer bytes32 internal immutable USER_BORROW_SLOT; // Can be of DEX or liquidity layer constructor(ConstantViews memory constants_) { TYPE = constants_.vaultType; if ( TYPE != FluidProtocolTypes.VAULT_T1_TYPE && TYPE != FluidProtocolTypes.VAULT_T2_SMART_COL_TYPE && TYPE != FluidProtocolTypes.VAULT_T3_SMART_DEBT_TYPE && TYPE != FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE ) { revert FluidVaultError(ErrorTypes.Vault__ImproperConstantsSetup); } LIQUIDITY = IFluidLiquidity(constants_.liquidity); VAULT_FACTORY = IFluidVaultFactory(constants_.factory); DEPLOYER_CONTRACT = constants_.deployer; SUPPLY = ILiquidityDexCommon(constants_.supply); BORROW = ILiquidityDexCommon(constants_.borrow); VAULT_ID = constants_.vaultId; OPERATE_IMPLEMENTATION = constants_.operateImplementation == address(0) ? address(this) : constants_.operateImplementation; // if smart collateral then adding dex address (even though it's not a token) else adding token address if ( TYPE == FluidProtocolTypes.VAULT_T2_SMART_COL_TYPE || TYPE == FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE ) { SUPPLY_TOKEN = constants_.supply; } else { SUPPLY_TOKEN = constants_.supplyToken.token0; if (constants_.supply != constants_.liquidity) { revert FluidVaultError(ErrorTypes.Vault__ImproperConstantsSetup); } } // if smart debt then adding dex address (even though it's not a token) else adding token address if ( TYPE == FluidProtocolTypes.VAULT_T3_SMART_DEBT_TYPE || TYPE == FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE ) { BORROW_TOKEN = constants_.borrow; } else { BORROW_TOKEN = constants_.borrowToken.token0; if (constants_.borrow != constants_.liquidity) { revert FluidVaultError(ErrorTypes.Vault__ImproperConstantsSetup); } } SUPPLY_TOKEN0 = constants_.supplyToken.token0; BORROW_TOKEN0 = constants_.borrowToken.token0; SUPPLY_TOKEN1 = constants_.supplyToken.token1; BORROW_TOKEN1 = constants_.borrowToken.token1; // below slots are calculated in the deploymentLogics / VaultFactory // if supply is directly on liquidity layer then liquidity layer storage slot else if supply is via DEX then bytes32(0) SUPPLY_EXCHANGE_PRICE_SLOT = constants_.supplyExchangePriceSlot; // if borrow is directly on liquidity layer then liquidity layer storage slot else if borrow is via DEX then bytes32(0) BORROW_EXCHANGE_PRICE_SLOT = constants_.borrowExchangePriceSlot; // if supply is directly on liquidity layer then liquidity layer storage slot else if supply is via DEX then dex storage slot USER_SUPPLY_SLOT = constants_.userSupplySlot; // if borrow is directly on liquidity layer then liquidity layer storage slot else if borrow is via DEX then dex storage slot USER_BORROW_SLOT = constants_.userBorrowSlot; ADMIN_IMPLEMENTATION = constants_.adminImplementation; SECONDARY_IMPLEMENTATION = constants_.secondaryImplementation; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; abstract contract Events { /// @notice emitted when an operate() method is executed that changes collateral (`colAmt_`) / debt (debtAmt_`) /// amount for a `user_` position with `nftId_`. Receiver of any funds is the address `to_`. event LogOperate(address user_, uint256 nftId_, int256 colAmt_, int256 debtAmt_, address to_); /// @notice emitted when the exchange prices are updated in storage. event LogUpdateExchangePrice(uint256 supplyExPrice_, uint256 borrowExPrice_); /// @notice emitted when a liquidation has been executed. event LogLiquidate(address liquidator_, uint256 colAmt_, uint256 debtAmt_, address to_); /// @notice emitted when `absorb()` was executed to absorb bad debt. event LogAbsorb(uint colAbsorbedRaw_, uint debtAbsorbedRaw_); /// @notice emitted when a `rebalance()` has been executed, balancing out total supply / borrow between Vault /// and Fluid Liquidity pools. /// if `colAmt_` is positive then loss, meaning transfer from rebalancer address to vault and deposit. /// if `colAmt_` is negative then profit, meaning withdrawn from vault and sent to rebalancer address. /// if `debtAmt_` is positive then profit, meaning borrow from vault and sent to rebalancer address. /// if `debtAmt_` is negative then loss, meaning transfer from rebalancer address to vault and payback. event LogRebalance(int colAmt_, int debtAmt_); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { Variables } from "../common/variables.sol"; import { TokenTransfers } from "../common/tokenTransfers.sol"; import { ConstantVariables } from "./constantVariables.sol"; import { Events } from "./events.sol"; import { TickMath } from "../../../../libraries/tickMath.sol"; import { BigMathMinified } from "../../../../libraries/bigMathMinified.sol"; import { BigMathVault } from "../../../../libraries/bigMathVault.sol"; import { LiquidityCalcs } from "../../../../libraries/liquidityCalcs.sol"; import { ErrorTypes } from "../../errorTypes.sol"; import { FluidProtocolTypes } from "../../../../libraries/fluidProtocolTypes.sol"; /// @dev Fluid vault protocol helper methods. Mostly used for `operate()` and `liquidate()` methods of CoreModule. abstract contract Helpers is Variables, ConstantVariables, Events, TokenTransfers { using BigMathMinified for uint256; using BigMathVault for uint256; modifier _dexFromAddress() { if (dexFromAddress != DEAD_ADDRESS) revert FluidVaultError(ErrorTypes.Vault__DexFromAddressAlreadySet); dexFromAddress = msg.sender; _; dexFromAddress = DEAD_ADDRESS; } /// @notice Calculates new vault exchange prices. Does not update values in storage. /// @param vaultVariables2_ exactly same as vaultVariables2 from storage /// @return liqSupplyExPrice_ latest liquidity's supply token supply exchange price /// @return liqBorrowExPrice_ latest liquidity's borrow token borrow exchange price /// @return vaultSupplyExPrice_ latest vault's supply token exchange price /// @return vaultBorrowExPrice_ latest vault's borrow token exchange price function updateExchangePrices( uint256 vaultVariables2_ ) public view returns ( uint256 liqSupplyExPrice_, uint256 liqBorrowExPrice_, uint256 vaultSupplyExPrice_, uint256 vaultBorrowExPrice_ ) { // Fetching last stored rates uint rates_ = rates; // in case of smart collateral oldLiqSupplyExPrice_ will be 0 uint256 oldLiqSupplyExPrice_ = (rates_ & X64); // in case of smart debt oldLiqBorrowExPrice_ will be 0 uint256 oldLiqBorrowExPrice_ = ((rates_ >> 64) & X64); uint timeStampDiff_ = block.timestamp - ((vaultVariables2_ >> 122) & X33); if ( TYPE == FluidProtocolTypes.VAULT_T2_SMART_COL_TYPE || TYPE == FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE ) { liqSupplyExPrice_ = EXCHANGE_PRICES_PRECISION; // in case of smart collateral supply magnifier bits stores, supply interest rate positive or negative // negative meaning charging users, positive means incentivizing users vaultSupplyExPrice_ = ((rates_ >> 128) & X64); // if 1 then positive else negative if ((vaultVariables2_ & 1) == 1) { vaultSupplyExPrice_ = vaultSupplyExPrice_ + (vaultSupplyExPrice_ * timeStampDiff_ * ((vaultVariables2_ >> 1) & X15)) / (10000 * LiquidityCalcs.SECONDS_PER_YEAR); } else { vaultSupplyExPrice_ = vaultSupplyExPrice_ - (vaultSupplyExPrice_ * timeStampDiff_ * ((vaultVariables2_ >> 1) & X15)) / (10000 * LiquidityCalcs.SECONDS_PER_YEAR); } } else { (liqSupplyExPrice_, ) = LiquidityCalcs.calcExchangePrices( LIQUIDITY.readFromStorage(SUPPLY_EXCHANGE_PRICE_SLOT) ); if (liqSupplyExPrice_ < oldLiqSupplyExPrice_) { // new liquidity exchange price is < than the old one. liquidity exchange price should only ever increase. // If not, something went wrong and avoid proceeding with unknown outcome. revert FluidVaultError(ErrorTypes.Vault__LiquidityExchangePriceUnexpected); } // liquidity Exchange Prices always increases in next block. Hence substraction with old will never be negative // uint64 * 1e18 is the max the number that could be unchecked { // Calculating increase in supply exchange price w.r.t last stored liquidity's exchange price // vaultSupplyExPrice_ => supplyIncreaseInPercent_ vaultSupplyExPrice_ = ((((liqSupplyExPrice_ * 1e18) / oldLiqSupplyExPrice_) - 1e18) * (vaultVariables2_ & X16)) / 10000; // supply rate magnifier // It's extremely hard the exchange prices to overflow even in 100 years but if it does it's not an // issue here as we are not updating on storage // (rates_ >> 128) & X64) -> last stored vault's supply token exchange price vaultSupplyExPrice_ = (((rates_ >> 128) & X64) * (1e18 + vaultSupplyExPrice_)) / 1e18; } } if ( TYPE == FluidProtocolTypes.VAULT_T3_SMART_DEBT_TYPE || TYPE == FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE ) { liqBorrowExPrice_ = EXCHANGE_PRICES_PRECISION; // in case of smart debt borrow magnifier bits stores, borrow interest rate positive or negative // negative meaning incentivizing users, positive means charging users vaultBorrowExPrice_ = ((rates_ >> 192) & X64); // if 1 then positive else negative if (((vaultVariables2_ >> 16) & 1) == 1) { vaultBorrowExPrice_ = vaultBorrowExPrice_ + (vaultBorrowExPrice_ * timeStampDiff_ * (((vaultVariables2_ >> 17) & X15))) / (10000 * LiquidityCalcs.SECONDS_PER_YEAR); } else { vaultBorrowExPrice_ = vaultBorrowExPrice_ - (vaultBorrowExPrice_ * timeStampDiff_ * (((vaultVariables2_ >> 17) & X15))) / (10000 * LiquidityCalcs.SECONDS_PER_YEAR); } } else { (, liqBorrowExPrice_) = LiquidityCalcs.calcExchangePrices( LIQUIDITY.readFromStorage(BORROW_EXCHANGE_PRICE_SLOT) ); if (liqBorrowExPrice_ < oldLiqBorrowExPrice_) { // new liquidity exchange price is < than the old one. liquidity exchange price should only ever increase. // If not, something went wrong and avoid proceeding with unknown outcome. revert FluidVaultError(ErrorTypes.Vault__LiquidityExchangePriceUnexpected); } // liquidity Exchange Prices always increases in next block. Hence substraction with old will never be negative // uint64 * 1e18 is the max the number that could be unchecked { // Calculating increase in borrow exchange price w.r.t last stored liquidity's exchange price // vaultBorrowExPrice_ => borrowIncreaseInPercent_ vaultBorrowExPrice_ = ((((liqBorrowExPrice_ * 1e18) / oldLiqBorrowExPrice_) - 1e18) * ((vaultVariables2_ >> 16) & X16)) / 10000; // borrow rate magnifier // It's extremely hard the exchange prices to overflow even in 100 years but if it does it's not an // issue here as we are not updating on storage // (rates_ >> 192) -> last stored vault's borrow token exchange price (no need to mask with & X64 as it is anyway max 64 bits) vaultBorrowExPrice_ = ((rates_ >> 192) * (1e18 + vaultBorrowExPrice_)) / 1e18; } } } /// @dev fetches new user's position after liquidation. The new liquidated position's debt is decreased by 0.01% /// 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. /// @param positionTick_ position's tick when it was last updated through operate /// @param positionTickId_ position's tick Id. This stores the debt factor and branch to make the first connection /// @param positionRawDebt_ position's raw debt when it was last updated through operate /// @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 /// @return final tick position after all the liquidation /// @return final debt of position after all the liquidation /// @return positionRawCol_ final collateral of position after all the liquidation /// @return branchId_ final branch's ID where the position is at currently /// @return branchData_ final branch's data where the position is at currently function fetchLatestPosition( int256 positionTick_, uint256 positionTickId_, uint256 positionRawDebt_, uint256 tickData_ ) public view returns ( int256, // positionTick_ uint256, // positionRawDebt_ uint256 positionRawCol_, uint256 branchId_, uint256 branchData_ ) { uint256 initialPositionRawDebt_ = positionRawDebt_; uint256 connectionFactor_; bool isFullyLiquidated_; // Checking if tick's total ID = user's tick ID if (((tickData_ >> 1) & X24) == positionTickId_) { // fetching from tick data itself isFullyLiquidated_ = ((tickData_ >> 25) & 1) == 1; branchId_ = (tickData_ >> 26) & X30; connectionFactor_ = (tickData_ >> 56) & X50; } else { { uint256 tickLiquidationData_; unchecked { // Fetching tick's liquidation data. One variable contains data of 3 IDs. Tick Id mapping is starting from 1. tickLiquidationData_ = tickId[positionTick_][(positionTickId_ + 2) / 3] >> (((positionTickId_ + 2) % 3) * 85); } isFullyLiquidated_ = (tickLiquidationData_ & 1) == 1; branchId_ = (tickLiquidationData_ >> 1) & X30; connectionFactor_ = (tickLiquidationData_ >> 31) & X50; } } // data of branch branchData_ = branchData[branchId_]; if (isFullyLiquidated_) { positionTick_ = type(int).min; positionRawDebt_ = 0; } else { // Below information about connection debt factor // If branch is merged, Connection debt factor is used to multiply in order to get perfect liquidation of user // For example: Considering user was at the top. // In first branch, the user liquidated to debt factor 0.5 and then branch got merged (branching starting from 1) // In second branch, it got liquidated to 0.4 but when the above branch merged the debt factor on this branch was 0.6 // Meaning on 1st branch, user got liquidated by 50% & on 2nd by 33.33%. So a total of 66.6%. // What we will set a connection factor will be 0.6/0.5 = 1.2 // So now to get user's position, this is what we'll do: // finalDebt = (0.4 / (1 * 1.2)) * debtBeforeLiquidation // 0.4 is current active branch's minima debt factor // 1 is debt factor from where user started // 1.2 is connection factor which we found out through 0.6 / 0.5 while ((branchData_ & 3) == 2) { // If true then the branch is merged // userTickDebtFactor * connectionDebtFactor *... connectionDebtFactor aka adjustmentDebtFactor connectionFactor_ = connectionFactor_.mulBigNumber(((branchData_ >> 116) & X50)); if (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR) break; // user ~100% liquidated // Note we don't need updated branch data in case of 100% liquidated so saving gas for fetching it // Fetching new branch data branchId_ = (branchData_ >> 166) & X30; // Link to base branch of current branch branchData_ = branchData[branchId_]; } // When the while loop breaks meaning the branch now has minima Debt Factor or is a closed branch; if (((branchData_ & 3) == 3) || (connectionFactor_ == BigMathVault.MAX_MASK_DEBT_FACTOR)) { // Branch got closed (or user liquidated ~100%). Hence make the user's position 0 // Rare cases to get into this situation // Branch can get close often but once closed it's tricky that some user might come iterating through there // If a user comes then that user will be very mini user like some cents probably positionTick_ = type(int).min; positionRawDebt_ = 0; } else { // If branch is not merged, the main branch it's connected to then it'll have minima debt factor // position debt = debt * base branch minimaDebtFactor / connectionFactor positionRawDebt_ = positionRawDebt_.mulDivNormal( (branchData_ >> 116) & X50, // minimaDebtFactor connectionFactor_ ); unchecked { // Reducing user's liquidity by 0.01% if user got liquidated. // As this will make sure that the branch always have some debt even if all liquidated user left // This saves a lot more logics & consideration on Operate function // if we don't do this then we have to add logics related to closing the branch and factor connections accordingly. if (positionRawDebt_ > (initialPositionRawDebt_ / 100)) { positionRawDebt_ = (positionRawDebt_ * 9999) / 10000; } else { // if user debt reduced by more than 99% in liquidation then making user as fully liquidated positionRawDebt_ = 0; } } { if (positionRawDebt_ > 0) { // positionTick_ -> read minima tick of branch unchecked { positionTick_ = branchData_ & 4 == 4 ? int((branchData_ >> 3) & X19) : -int((branchData_ >> 3) & X19); } // Calculating user's collateral uint256 ratioAtTick_ = TickMath.getRatioAtTick(int24(positionTick_)); uint256 ratioOneLess_; unchecked { ratioOneLess_ = (ratioAtTick_ * 10000) / 10015; } // formula below for better readability: // length = ratioAtTick_ - ratioOneLess_ // ratio = ratioOneLess_ + (length * positionPartials_) / X30 // positionRawCol_ = (positionRawDebt_ * (1 << 96)) / ratio_ positionRawCol_ = (positionRawDebt_ * TickMath.ZERO_TICK_SCALED_RATIO) / (ratioOneLess_ + ((ratioAtTick_ - ratioOneLess_) * ((branchData_ >> 22) & X30)) / X30); } else { positionTick_ = type(int).min; } } } } return (positionTick_, positionRawDebt_, positionRawCol_, branchId_, branchData_); } constructor(ConstantViews memory constants_) ConstantVariables(constants_) {} }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { Helpers } from "./helpers.sol"; import { ErrorTypes } from "../../errorTypes.sol"; /// @dev Fluid vault protocol helper methods. Mostly used for `operate()` and `liquidate()` methods of CoreModule. abstract contract HelpersLiquidate is Helpers { /// note admin module is also calling this function self call /// @dev updating exchange price on storage. Only need to update on storage when changing supply or borrow magnifier function updateExchangePricesOnStorage() public returns ( uint256 liqSupplyExPrice_, uint256 liqBorrowExPrice_, uint256 vaultSupplyExPrice_, uint256 vaultBorrowExPrice_ ) { (liqSupplyExPrice_, liqBorrowExPrice_, vaultSupplyExPrice_, vaultBorrowExPrice_) = updateExchangePrices( vaultVariables2 ); if ( liqSupplyExPrice_ > X64 || liqBorrowExPrice_ > X64 || vaultSupplyExPrice_ > X64 || vaultBorrowExPrice_ > X64 ) { revert FluidVaultError(ErrorTypes.Vault__ExchangePriceOverFlow); } // Updating in storage rates = liqSupplyExPrice_ | (liqBorrowExPrice_ << 64) | (vaultSupplyExPrice_ << 128) | (vaultBorrowExPrice_ << 192); vaultVariables2 = (vaultVariables2 & 0xfffffffffffffffffffffffff800000003ffffffffffffffffffffffffffffff) | (block.timestamp << 122); emit LogUpdateExchangePrice(vaultSupplyExPrice_, vaultBorrowExPrice_); } constructor(ConstantViews memory constants_) Helpers(constants_) {} }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { IFluidOracle } from "../../../../oracle/fluidOracle.sol"; import { TickMath } from "../../../../libraries/tickMath.sol"; import { BigMathMinified } from "../../../../libraries/bigMathMinified.sol"; import { BigMathVault } from "../../../../libraries/bigMathVault.sol"; import { SafeTransfer } from "../../../../libraries/safeTransfer.sol"; import { HelpersLiquidate } from "./helpersLiquidate.sol"; import { LiquiditySlotsLink } from "../../../../libraries/liquiditySlotsLink.sol"; import { FluidProtocolTypes } from "../../../../libraries/fluidProtocolTypes.sol"; import { ErrorTypes } from "../../errorTypes.sol"; import { AddressCalcs } from "../../../../libraries/addressCalcs.sol"; /// @notice Fluid vault protocol main contract base. /// Fluid Vault protocol is a borrow / lending protocol, allowing users to create collateral / borrow positions. /// All funds are deposited into / borrowed from Fluid Liquidity layer. /// Positions are represented through NFTs minted by the VaultFactory. /// Deployed by "VaultFactory" and linked together with Vault AdminModule `ADMIN_IMPLEMENTATION` and /// FluidVaultSecondary (main2.sol) `SECONDARY_IMPLEMENTATION`. /// AdminModule & FluidVaultSecondary methods are delegateCalled, if the msg.sender has the required authorization. /// This contract links to an Oracle, which is used to assess collateral / debt value. Oracles implement the /// "FluidOracle" base contract and return the price in 1e27 precision. /// @dev For view methods / accessing data, use the "VaultResolver" periphery contract. // // vaults can only be deployed for tokens that are listed at Liquidity (constructor reverts otherwise // if either the exchange price for the supply token or the borrow token is still not set at Liquidity). abstract contract FluidVault is HelpersLiquidate { using BigMathMinified for uint256; using BigMathVault for uint256; function simulateLiquidate(uint debtAmt_, bool absorb_) external { uint vaultVariables_ = vaultVariables; // ############# turning re-entrancy bit on ############# if (vaultVariables_ & 1 == 0) { // Updating on storage vaultVariables = vaultVariables_ | 1; } else { revert FluidVaultError(ErrorTypes.Vault__AlreadyEntered); } debtAmt_ = debtAmt_ == 0 ? X128 : debtAmt_; _liquidate(X128, 0, DEAD_ADDRESS, absorb_, vaultVariables_); // this revert will never reach as the revert is inside the liquidate function due to to_ = DEAD_ADDRESS // but still added just to be extra safe revert(); } /// @dev allows to liquidate all bad debt of all users at once. Liquidator can also liquidate partially any amount they want. /// @param debtAmt_ total debt to liquidate (aka debt token to swap into collateral token) /// @param colPerUnitDebt_ minimum collateral token per unit of debt in 1e18 decimals /// @param to_ address at which collateral token should go to. /// If dead address (DEAD_ADDRESS) then reverts with custom error "FluidLiquidateResult" /// returning the actual collateral and actual debt liquidated. Useful to find max liquidatable amounts via try / catch. /// @param absorb_ if true then liquidate from absorbed first /// @param vaultVariables_ the current state of the vaultVariables from storage /// @return bytes with 3 uints, r_[0] = actualDebtAmt, r_[1] = actualColAmt, r_[2] = vaultVariables_ /// actualDebtAmt if liquidator sends debtAmt_ more than debt remaining to liquidate then actualDebtAmt changes from debtAmt_ else remains same /// actualColAmt total liquidated collateral which liquidator will get function _liquidate( uint256 debtAmt_, uint256 colPerUnitDebt_, // min collateral needed per unit of debt in 1e18 address to_, bool absorb_, uint vaultVariables_ ) internal returns (bytes memory) { LiquidateMemoryVars memory memoryVars_; memoryVars_.vaultVariables2 = vaultVariables2; if (((vaultVariables_ >> 2) & X20) == 0) { revert FluidVaultError(ErrorTypes.Vault__TopTickDoesNotExist); } // Below are exchange prices of vaults (, , memoryVars_.supplyExPrice, memoryVars_.borrowExPrice) = updateExchangePrices(memoryVars_.vaultVariables2); CurrentLiquidity memory currentData_; BranchData memory branch_; // Temporary holder variables, used many times for different small things uint temp_; uint temp2_; { // ############# Oracle related stuff ############# // Col price w.r.t debt. For example: 1 ETH = 1000 DAI // temp_ -> debtPerCol temp_ = IFluidOracle( AddressCalcs.addressCalc(DEPLOYER_CONTRACT, ((memoryVars_.vaultVariables2 >> 92) & X30)) ).getExchangeRateLiquidate(); // Price in 27 decimals // not reverting if oracle price is lower than 1e9 as it can pause potential liquidation in this edge case situations if (temp_ > 1e54 || temp_ == 0) { revert FluidVaultError(ErrorTypes.Vault__InvalidOraclePrice); } unchecked { // temp_ -> debtPerCol Converting in terms of raw amount temp_ = (temp_ * memoryVars_.supplyExPrice) / memoryVars_.borrowExPrice; // capping oracle pricing to 1e45 // Reason mentioned at (search: #487RGF783GF) if (temp_ > 1e45) { temp_ = 1e45; } // temp2_ -> Raw colPerDebt_ in 27 decimals temp2_ = 1e54 / temp_; // temp2_ can never be > 1e54 // Oracle price should never be > 1e54 // Liquidation penalty in 4 decimals (1e2 = 1%) (max: 10.23%) -> (vaultVariables2_ >> 72) & X10 currentData_.colPerDebt = (temp2_ * (10000 + ((memoryVars_.vaultVariables2 >> 72) & X10))) / 10000; // get liquidiation tick (tick at liquidation threshold ratio) // Liquidation threshold in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 42) & X10 // Dividing by 1e27 to convert temp_ into normal number temp_ = ((temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / 1e27); // temp2_ -> liquidationRatio_ temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 42) & X10)) / 1000; } (memoryVars_.liquidationTick, ) = TickMath.getTickAtRatio(temp2_); // get liquidiation max limit tick (tick at liquidation max limit ratio) // Max limit in 3 decimals (900 = 90%) -> (vaultVariables2_ >> 52) & X10 // temp2_ -> maxRatio_ unchecked { temp2_ = (temp_ * ((memoryVars_.vaultVariables2 >> 52) & X10)) / 1000; } (memoryVars_.maxTick, ) = TickMath.getTickAtRatio(temp2_); } // extracting top tick as top tick will be the current tick unchecked { currentData_.tick = (vaultVariables_ & 4) == 4 ? int256((vaultVariables_ >> 3) & X19) : -int256((vaultVariables_ >> 3) & X19); } if (currentData_.tick > memoryVars_.maxTick) { // absorbing all the debt above maxTick if available vaultVariables_ = ( abi.decode( _spell( SECONDARY_IMPLEMENTATION, abi.encodeWithSignature("absorb(uint256,int256)", vaultVariables_, memoryVars_.maxTick) ), (uint256) ) ); // updating current tick to new topTick after absorb unchecked { currentData_.tick = (vaultVariables_ & 4) == 4 ? int256((vaultVariables_ >> 3) & X19) : -int256((vaultVariables_ >> 3) & X19); } if (debtAmt_ == 0) { // updating vault variables on storage as the transaction was for only absorb // Vault variables is getting updated through liquidate function return abi.encode(0, 0, vaultVariables_); } } if (debtAmt_ < 10000 || debtAmt_ > X128) { revert FluidVaultError(ErrorTypes.Vault__InvalidLiquidationAmt); } // setting up status if top tick is liquidated or not currentData_.tickStatus = vaultVariables_ & 2 == 0 ? 1 : 2; // Tick info is mainly used as a place holder to store temporary tick related data // (it can be current or ref using same memory variable) TickData memory tickInfo_; tickInfo_.tick = currentData_.tick; { // ############# Setting current branch in memory ############# // Updating branch related data branch_.id = (vaultVariables_ >> 22) & X30; branch_.data = branchData[branch_.id]; branch_.debtFactor = (branch_.data >> 116) & X50; if (branch_.debtFactor == 0) { // Initializing branch debt factor. 35 | 15 bit number. Where full 35 bits and 15th bit is occupied. // Making the total number as (2**35 - 1) << 2**14. // note: initial debt factor can be any number. branch_.debtFactor = ((X35 << 15) | (1 << 14)); } // fetching base branch's minima tick. if 0 that means it's a master branch temp_ = (branch_.data >> 196) & X20; if (temp_ > 0) { unchecked { branch_.minimaTick = (temp_ & 1) == 1 ? int256((temp_ >> 1) & X19) : -int256((temp_ >> 1) & X19); } } else { branch_.minimaTick = type(int).min; } } // debtAmt_ should be less than 2**128 & EXCHANGE_PRICES_PRECISION is 1e12 unchecked { currentData_.debtRemaining = (debtAmt_ * EXCHANGE_PRICES_PRECISION) / memoryVars_.borrowExPrice; } // extracting total debt temp2_ = (vaultVariables_ >> 146) & X64; temp2_ = ((temp2_ >> 8) << (temp2_ & X8)); if ((temp2_ / 1e9) > currentData_.debtRemaining) { // if liquidation amount is less than 1e9 of total debt then revert // so if total debt is $1B then minimum liquidation limit = $1 // so if total debt is $1T then minimum liquidation limit = $1000 // partials precision is slightlty above 1e9 so this will make sure that on every liquidation atleast 1 partial gets liquidated // not sure if it can result in any issue but restricting amount further more to remove very low amount scenarios totally revert FluidVaultError(ErrorTypes.Vault__InvalidLiquidationAmt); } if (absorb_) { temp_ = absorbedLiquidity; // temp2_ -> absorbed col temp2_ = (temp_ >> 128) & X128; // temp_ -> absorbed debt temp_ = temp_ & X128; if (temp_ > currentData_.debtRemaining) { // Removing collateral in equal proportion as debt currentData_.totalColLiq = ((temp2_ * currentData_.debtRemaining) / temp_); temp2_ -= currentData_.totalColLiq; // Removing debt currentData_.totalDebtLiq = currentData_.debtRemaining; unchecked { temp_ -= currentData_.debtRemaining; } currentData_.debtRemaining = 0; // updating on storage absorbedLiquidity = temp_ | (temp2_ << 128); } else { // updating on storage absorbedLiquidity = 0; unchecked { currentData_.debtRemaining -= temp_; } currentData_.totalDebtLiq = temp_; currentData_.totalColLiq = temp2_; } } // current tick should be greater than liquidationTick and it cannot be greater than maxTick as absorb will run if (currentData_.tick > memoryVars_.liquidationTick) { if (currentData_.debtRemaining > 0) { // Stores liquidated debt & collateral in each loop uint debtLiquidated_; uint colLiquidated_; uint debtFactor_ = BigMathVault.TWO_POWER_64; TickHasDebt memory tickHasDebt_; unchecked { tickHasDebt_.mapId = (currentData_.tick < 0) ? (((currentData_.tick + 1) / 256) - 1) : (currentData_.tick / 256); } tickInfo_.ratio = TickMath.getRatioAtTick(tickInfo_.tick); if (currentData_.tickStatus == 1) { // top tick is not liquidated. Hence it's a perfect tick. currentData_.ratio = tickInfo_.ratio; // if current tick in liquidation is a perfect tick then it is also the next tick that has debt. tickHasDebt_.nextTick = currentData_.tick; } else { // top tick is liquidated. Hence it has partials. // next tick that has debt liquidity will have to be fetched from tickHasDebt unchecked { tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015; tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess; tickInfo_.partials = (branch_.data >> 22) & X30; currentData_.ratio = tickInfo_.ratioOneLess + ((tickInfo_.length * tickInfo_.partials) / X30); if ((memoryVars_.liquidationTick + 1) == tickInfo_.tick && (tickInfo_.partials == 1)) { if (to_ == DEAD_ADDRESS) { // revert with liquidated amounts if to_ address is the dead address. // this can be used in a resolver to find the max liquidatable amounts. revert FluidLiquidateResult(0, 0); } revert FluidVaultError(ErrorTypes.Vault__InvalidLiquidation); } } } while (true) { if (currentData_.tickStatus == 1) { // not liquidated -> Getting the debt from tick data itself temp2_ = tickData[currentData_.tick]; // temp_ => tick debt temp_ = (temp2_ >> 25) & X64; // Converting big number into normal number temp_ = (temp_ >> 8) << (temp_ & X8); // Updating tickData on storage with removing debt & adding connection to branch tickData[currentData_.tick] = 1 | // set tick as liquidated (temp2_ & 0x1fffffe) | // set same total tick ids (branch_.id << 26) | // branch id where this tick got liquidated (branch_.debtFactor << 56); } else { // already liquidated -> Get the debt from branch data in big number // temp_ => tick debt temp_ = (branch_.data >> 52) & X64; // Converting big number into normal number temp_ = (temp_ >> 8) << (temp_ & X8); // Branch is getting updated over the end } // Adding new debt into active debt for liquidation currentData_.debt += temp_; // Adding new col into active col for liquidation // Ratio is in 2**96 decimals hence multiplying debt with 2**96 to get proper collateral currentData_.col += (temp_ * TickMath.ZERO_TICK_SCALED_RATIO) / currentData_.ratio; if ( (tickHasDebt_.nextTick == currentData_.tick && currentData_.tickStatus == 1) || tickHasDebt_.tickHasDebt == 0 ) { // Fetching next perfect tick with liquidity // tickHasDebt_.tickHasDebt == 0 will only happen in the first while loop // in the very first perfect tick liquidation it'll be 0 if (tickHasDebt_.tickHasDebt == 0) { tickHasDebt_.tickHasDebt = tickHasDebt[tickHasDebt_.mapId]; } // in 1st loop tickStatus can be 2. Meaning not a perfect current tick if (currentData_.tickStatus == 1) { unchecked { tickHasDebt_.bitsToRemove = uint(-currentData_.tick + (tickHasDebt_.mapId * 256 + 256)); } // Removing current top tick from tickHasDebt tickHasDebt_.tickHasDebt = (tickHasDebt_.tickHasDebt << tickHasDebt_.bitsToRemove) >> tickHasDebt_.bitsToRemove; // Updating in storage if tickHasDebt becomes 0. if (tickHasDebt_.tickHasDebt == 0) { tickHasDebt[tickHasDebt_.mapId] = 0; } } // For last user remaining in vault there could be a lot of while loop. // Chances of this to happen is extremely low (like ~0%) while (true) { if (tickHasDebt_.tickHasDebt > 0) { unchecked { tickHasDebt_.nextTick = tickHasDebt_.mapId * 256 + int(tickHasDebt_.tickHasDebt.mostSignificantBit()) - 1; } break; } // tickHasDebt_.tickHasDebt == 0. Checking if minimum tick of this mapID is less than liquidationTick_ // if true that means now the next tick is not needed as liquidation gets over minimum at liquidationTick_ unchecked { if ((tickHasDebt_.mapId * 256) < memoryVars_.liquidationTick) { tickHasDebt_.nextTick = type(int).min; break; } // Fetching next tick has debt by decreasing tickHasDebt_.mapId first tickHasDebt_.tickHasDebt = tickHasDebt[--tickHasDebt_.mapId]; } } } // Fetching refTick. refTick is the biggest tick of these 3: // 1. Next tick with liquidity (from tickHasDebt) // 2. Minima tick of current branch // 3. Liquidation threshold tick { // Setting currentData_.refTick & currentData_.refTickStatus if ( branch_.minimaTick > tickHasDebt_.nextTick && branch_.minimaTick > memoryVars_.liquidationTick ) { // next tick will be of base branch (merge) currentData_.refTick = branch_.minimaTick; currentData_.refTickStatus = 2; } else if (tickHasDebt_.nextTick > memoryVars_.liquidationTick) { // next tick will be next tick from perfect tick currentData_.refTick = tickHasDebt_.nextTick; currentData_.refTickStatus = 1; } else { // next tick is threshold tick currentData_.refTick = memoryVars_.liquidationTick; currentData_.refTickStatus = 3; // leads to end of liquidation loop } } // using tickInfo variable again for ref tick as we don't have the need for it any more tickInfo_.ratio = TickMath.getRatioAtTick(int24(currentData_.refTick)); if (currentData_.refTickStatus == 2) { // merge current branch with base branch unchecked { tickInfo_.ratioOneLess = (tickInfo_.ratio * 10000) / 10015; tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess; // Fetching base branch data to get the base branch's partial branch_.baseBranchData = branchData[((branch_.data >> 166) & X30)]; tickInfo_.partials = (branch_.baseBranchData >> 22) & X30; tickInfo_.currentRatio = tickInfo_.ratioOneLess + ((tickInfo_.length * tickInfo_.partials) / X30); currentData_.refRatio = tickInfo_.currentRatio; } } else { // refTickStatus can only be 1 (next tick from perfect tick) or 3 (liquidation threshold tick) tickInfo_.currentRatio = tickInfo_.ratio; currentData_.refRatio = tickInfo_.ratio; tickInfo_.partials = X30; } // Formula: (debt_ - x) / (col_ - (x * colPerDebt_)) = ratioEnd_ // x = ((ratioEnd_ * col) - debt_) / ((colPerDebt_ * ratioEnd_) - 1) // x is debtToLiquidate_ // col_ = debt_ / ratioStart_ -> (currentData_.debt / currentData_.ratio) // ratioEnd_ is currentData_.refRatio // // Calculation results of numerator & denominator is always negative // which will cancel out to give positive output in the end so we can safely cast to uint. // for nominator: // ratioStart can only be >= ratioEnd so first part can only be reducing currentData_.debt leading to // currentData_.debt reduced - currentData_.debt original * 1e27 -> can only be a negative number // for denominator: // currentData_.colPerDebt and currentData_.refRatio are inversely proportional to each other. // the maximum value they can ever be is ~9.97e26 which is the 0.3% away from 100% because liquidation // threshold + liquidation penalty can never be > 99.7%. This can also be verified by going back from // min / max ratio values further up where we fetch oracle price etc. // as optimization we can inverse nominator and denominator subtraction to directly get a positive number. debtLiquidated_ = // nominator ((currentData_.debt - (currentData_.refRatio * currentData_.debt) / currentData_.ratio) * 1e27) / // denominator (1e27 - ((currentData_.colPerDebt * currentData_.refRatio) / TickMath.ZERO_TICK_SCALED_RATIO)); colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27; if (currentData_.debt == debtLiquidated_) { debtLiquidated_ -= 1; } if (debtLiquidated_ >= currentData_.debtRemaining || currentData_.refTickStatus == 3) { // End of liquidation as full amount to liquidate or liquidation threshold tick has been reached; // Updating tickHasDebt on storage. tickHasDebt[tickHasDebt_.mapId] = tickHasDebt_.tickHasDebt; if (debtLiquidated_ >= currentData_.debtRemaining) { // Liquidation ended between currentTick & refTick. // Not all of liquidatable debt is actually liquidated -> recalculate debtLiquidated_ = currentData_.debtRemaining; colLiquidated_ = (debtLiquidated_ * currentData_.colPerDebt) / 1e27; // Liquidating to debt. temp_ => final ratio after liquidation // liquidatable debt - debtLiquidated / liquidatable col - colLiquidated temp_ = ((currentData_.debt - debtLiquidated_) * TickMath.ZERO_TICK_SCALED_RATIO) / (currentData_.col - colLiquidated_); // Fetching tick of where liquidation ended (tickInfo_.tick, tickInfo_.ratioOneLess) = TickMath.getTickAtRatio(temp_); if ((tickInfo_.tick < currentData_.refTick) && (tickInfo_.partials == X30)) { // this situation might never happen // if this happens then there might be some very edge case precision of few weis which is returning 1 tick less // if the above were to ever happen then tickInfo_.tick only be currentData_.refTick - 1 // in this case the partial will be very very near to full (X30) // increasing tick by 2 and making partial as 1 which is basically very very near to currentData_.refTick unchecked { tickInfo_.tick += 2; } tickInfo_.partials = 1; } else { unchecked { // Increasing tick by 1 as final ratio will probably be a partial ++tickInfo_.tick; // if ref tick is old liquidated tick then storing partials in temp2_ // tickInfo_.partials contains partial of branch which is the current ref tick temp2_ = (currentData_.refTickStatus == 2 && tickInfo_.tick == currentData_.refTick) ? tickInfo_.partials : 0; tickInfo_.ratio = (tickInfo_.ratioOneLess * 10015) / 10000; tickInfo_.length = tickInfo_.ratio - tickInfo_.ratioOneLess; tickInfo_.partials = ((temp_ - tickInfo_.ratioOneLess) * X30) / tickInfo_.length; // Taking edge cases where partial comes as 0 or X30 meaning perfect tick. // Hence, increasing or reducing it by 1 as liquidation tick cannot be perfect tick. tickInfo_.partials = tickInfo_.partials == 0 ? 1 : tickInfo_.partials >= X30 ? X30 - 1 : tickInfo_.partials; } if (temp2_ > 0 && temp2_ >= tickInfo_.partials) { // if refTick is liquidated tick and hence contains partials then checking that // current liquidation tick's partial should not be less than last liquidation refTick // not sure if this is even possible to happen but adding checks to avoid it fully // if it reverts here then next liquidation on next block should go through fine revert FluidVaultError(ErrorTypes.Vault__LiquidationReverts); } } } else { // End in liquidation threshold. // finalRatio_ = currentData_.refRatio; // Increasing liquidation threshold tick by 1 partial. With 1 partial it'll reach to the next tick. // Ratio change will be negligible. Doing this as liquidation threshold tick can also be a perfect non-liquidated tick. unchecked { tickInfo_.tick = currentData_.refTick + 1; } // Making partial as 1 so it doesn't stay perfect tick tickInfo_.partials = 1; // length is not needed as only partials are written to storage } // debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt // -> debtFactor * leftOverDebt / liquidatableDebt debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt; currentData_.totalDebtLiq += debtLiquidated_; currentData_.debt -= debtLiquidated_; // currentData_.debt => leftOverDebt after debtLiquidated_ currentData_.totalColLiq += colLiquidated_; currentData_.col -= colLiquidated_; // currentData_.col => leftOverCol after colLiquidated_ // Updating branch's debt factor & write to storage as liquidation is over branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_); if (currentData_.debt < 100) { // this can happen when someone tries to create a dust tick revert FluidVaultError(ErrorTypes.Vault__BranchDebtTooLow); } unchecked { // Tick to insert temp2_ = tickInfo_.tick < 0 ? (uint(-tickInfo_.tick) << 1) : ((uint(tickInfo_.tick) << 1) | 1); } // Updating Branch data with debt factor, debt, partials, minima tick & assigning is liquidated branchData[branch_.id] = ((branch_.data >> 166) << 166) | 1 | // set as liquidated (temp2_ << 2) | // minima tick of branch (tickInfo_.partials << 22) | (currentData_.debt.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 52) | // branch debt (branch_.debtFactor << 116); // Updating vault variables with current branch & tick vaultVariables_ = ((vaultVariables_ >> 52) << 52) | 2 | // set as liquidated (temp2_ << 2) | // top tick (branch_.id << 22); break; } unchecked { // debtLiquidated_ >= currentData_.debtRemaining leads to loop break in if statement above // so this can be unchecked currentData_.debtRemaining -= debtLiquidated_; } // debtFactor = debtFactor * (liquidatableDebt - debtLiquidated) / liquidatableDebt // -> debtFactor * leftOverDebt / liquidatableDebt debtFactor_ = (debtFactor_ * (currentData_.debt - debtLiquidated_)) / currentData_.debt; currentData_.totalDebtLiq += debtLiquidated_; currentData_.debt -= debtLiquidated_; currentData_.totalColLiq += colLiquidated_; currentData_.col -= colLiquidated_; // updating branch's debt factor branch_.debtFactor = branch_.debtFactor.mulDivBigNumber(debtFactor_); // Setting debt factor as 1 << 64 again debtFactor_ = BigMathVault.TWO_POWER_64; if (currentData_.refTickStatus == 2) { // ref tick is base branch's minima hence merging current branch to base branch // and making base branch as current branch. // read base branch related data temp_ = (branch_.data >> 166) & X30; // temp_ -> base branch id temp2_ = branch_.baseBranchData; { uint newBranchDebtFactor_ = (temp2_ >> 116) & X50; // connectionFactor_ = baseBranchDebtFactor / currentBranchDebtFactor uint connectionFactor_ = newBranchDebtFactor_.divBigNumber(branch_.debtFactor); // Updating current branch in storage branchData[branch_.id] = ((branch_.data >> 166) << 166) | // deleting debt / partials / minima tick 2 | // setting as merged (connectionFactor_ << 116); // set new connectionFactor // Storing base branch in memory // Updating branch ID to base branch ID branch_.id = temp_; // Updating branch data with base branch data branch_.data = temp2_; // Remove next branch connection from base branch branch_.debtFactor = newBranchDebtFactor_; // temp_ => minima tick of base branch temp_ = (temp2_ >> 196) & X20; if (temp_ > 0) { unchecked { branch_.minimaTick = (temp_ & 1) == 1 ? int256((temp_ >> 1) & X19) : -int256((temp_ >> 1) & X19); } } else { branch_.minimaTick = type(int).min; } } } // Making refTick as currentTick currentData_.tick = currentData_.refTick; currentData_.tickStatus = currentData_.refTickStatus; currentData_.ratio = currentData_.refRatio; } } } // calculating net token amounts using exchange price memoryVars_.actualDebtAmt = (currentData_.totalDebtLiq * memoryVars_.borrowExPrice) / EXCHANGE_PRICES_PRECISION; memoryVars_.actualColAmt = (currentData_.totalColLiq * memoryVars_.supplyExPrice) / EXCHANGE_PRICES_PRECISION; // Chances of this to happen are in few wei if (memoryVars_.actualDebtAmt > debtAmt_) { // calc new memoryVars_.actualColAmt via ratio. memoryVars_.actualColAmt = memoryVars_.actualColAmt * (debtAmt_ / memoryVars_.actualDebtAmt); memoryVars_.actualDebtAmt = debtAmt_; } if (memoryVars_.actualDebtAmt == 0) { revert FluidVaultError(ErrorTypes.Vault__InvalidLiquidation); } if (((memoryVars_.actualColAmt * 1e18) / memoryVars_.actualDebtAmt) < colPerUnitDebt_) { revert FluidVaultError(ErrorTypes.Vault__ExcessSlippageLiquidation); } if (to_ == DEAD_ADDRESS) { // revert with liquidated amounts if to_ address is the dead address. // this can be used in a resolver to find the max liquidatable amounts. revert FluidLiquidateResult(memoryVars_.actualColAmt, memoryVars_.actualDebtAmt); } if ( !(TYPE == FluidProtocolTypes.VAULT_T3_SMART_DEBT_TYPE || TYPE == FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE) ) { // payback at Liquidity if (BORROW_TOKEN == NATIVE_TOKEN) { temp_ = memoryVars_.actualDebtAmt; } else { temp_ = 0; } // payback at liquidity LIQUIDITY.operate{ value: temp_ }( BORROW_TOKEN, 0, -int(memoryVars_.actualDebtAmt), address(0), address(0), abi.encode(msg.sender) ); } if ( !(TYPE == FluidProtocolTypes.VAULT_T2_SMART_COL_TYPE || TYPE == FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE) ) { // withdraw at liquidity LIQUIDITY.operate(SUPPLY_TOKEN, -int(memoryVars_.actualColAmt), 0, to_, address(0), new bytes(0)); } // Calculating new total collateral & total debt. // temp_ -> total supply temp_ = (vaultVariables_ >> 82) & X64; temp_ = ((temp_ >> 8) << (temp_ & X8)) - currentData_.totalColLiq; // temp2_ -> total borrow temp2_ = (vaultVariables_ >> 146) & X64; temp2_ = ((temp2_ >> 8) << (temp2_ & X8)) - currentData_.totalDebtLiq; // Updating vault variables on storage // Converting total supply & total borrow in 64 bits (56 | 8) bignumber vaultVariables_ = (vaultVariables_ & 0xfffffffffffc00000000000000000000000000000003ffffffffffffffffffff) | (temp_.toBigNumber(56, 8, BigMathMinified.ROUND_DOWN) << 82) | // total supply (temp2_.toBigNumber(56, 8, BigMathMinified.ROUND_UP) << 146); // total borrow emit LogLiquidate(msg.sender, memoryVars_.actualColAmt, memoryVars_.actualDebtAmt, to_); return abi.encode(memoryVars_.actualDebtAmt, memoryVars_.actualColAmt, vaultVariables_); } /// @dev Checks total supply of vault's in Liquidity Layer & Vault contract and rebalance it accordingly /// if vault supply is more than LiquidityLayer/DEX then deposit difference through reserve/rebalance contract /// if vault supply is less than LiquidityLayer/DEX then withdraw difference to reserve/rebalance contract /// if vault borrow is more than LiquidityLayer/DEX then borrow difference to reserve/rebalance contract /// if vault borrow is less than LiquidityLayer/DEX then payback difference through reserve/rebalance contract function rebalance(int, int, int, int) external payable _dexFromAddress returns (int supplyAmt_, int borrowAmt_) { (supplyAmt_, borrowAmt_) = abi.decode(_spell(SECONDARY_IMPLEMENTATION, msg.data), (int, int)); } /// @dev liquidity callback for cheaper token transfers in case of deposit or payback. /// only callable by Liquidity during an operation. function liquidityCallback(address token_, uint amount_, bytes calldata data_) external { if (msg.sender != address(LIQUIDITY)) { revert FluidVaultError(ErrorTypes.Vault__InvalidLiquidityCallbackAddress); } if (vaultVariables & 1 == 0) revert FluidVaultError(ErrorTypes.Vault__NotEntered); SafeTransfer.safeTransferFrom(token_, abi.decode(data_, (address)), address(LIQUIDITY), amount_); } /// @dev dex callback for cheaper token transfers in case of deposit or payback. /// only callable by dex during an operation. function dexCallback(address token_, uint amount_) external { if (!(msg.sender == address(SUPPLY) || msg.sender == address(BORROW))) { revert FluidVaultError(ErrorTypes.Vault__InvalidDexCallbackAddress); } if (vaultVariables & 1 == 0) revert FluidVaultError(ErrorTypes.Vault__NotEntered); SafeTransfer.safeTransferFrom(token_, dexFromAddress, address(LIQUIDITY), amount_); } /// @notice returns all Vault constants function constantsView() external view returns (ConstantViews memory constantsView_) { constantsView_.liquidity = address(LIQUIDITY); constantsView_.factory = address(VAULT_FACTORY); constantsView_.operateImplementation = OPERATE_IMPLEMENTATION; constantsView_.adminImplementation = ADMIN_IMPLEMENTATION; constantsView_.secondaryImplementation = SECONDARY_IMPLEMENTATION; constantsView_.deployer = DEPLOYER_CONTRACT; constantsView_.supply = address(SUPPLY); constantsView_.borrow = address(BORROW); constantsView_.supplyToken.token0 = SUPPLY_TOKEN0; constantsView_.supplyToken.token1 = SUPPLY_TOKEN1; constantsView_.borrowToken.token0 = BORROW_TOKEN0; constantsView_.borrowToken.token1 = BORROW_TOKEN1; constantsView_.vaultId = VAULT_ID; constantsView_.vaultType = TYPE; constantsView_.supplyExchangePriceSlot = SUPPLY_EXCHANGE_PRICE_SLOT; constantsView_.borrowExchangePriceSlot = BORROW_EXCHANGE_PRICE_SLOT; constantsView_.userSupplySlot = USER_SUPPLY_SLOT; constantsView_.userBorrowSlot = USER_BORROW_SLOT; } constructor(ConstantViews memory constants_) HelpersLiquidate(constants_) { // Note that vaults are deployed by VaultFactory so we somewhat trust the values being passed in // Setting branch in vault. vaultVariables = (vaultVariables) | (1 << 22) | (1 << 52); dexFromAddress = DEAD_ADDRESS; // If smart collateral then liqSupplyExchangePrice_ will always be EXCHANGE_PRICES_PRECISION uint liqSupplyExchangePrice_ = (constants_.vaultType == FluidProtocolTypes.VAULT_T2_SMART_COL_TYPE || constants_.vaultType == FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE) ? EXCHANGE_PRICES_PRECISION : ((SUPPLY.readFromStorage(SUPPLY_EXCHANGE_PRICE_SLOT) >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64); // If smart debt then liqBorrowExchangePrice_ will always be EXCHANGE_PRICES_PRECISION uint liqBorrowExchangePrice_ = (constants_.vaultType == FluidProtocolTypes.VAULT_T3_SMART_DEBT_TYPE || constants_.vaultType == FluidProtocolTypes.VAULT_T4_SMART_COL_SMART_DEBT_TYPE) ? EXCHANGE_PRICES_PRECISION : ((BORROW.readFromStorage(BORROW_EXCHANGE_PRICE_SLOT) >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64); if ( liqSupplyExchangePrice_ < EXCHANGE_PRICES_PRECISION || liqBorrowExchangePrice_ < EXCHANGE_PRICES_PRECISION ) { revert FluidVaultError(ErrorTypes.Vault__TokenNotInitialized); } if (constants_.operateImplementation == address(0)) { revert FluidVaultError(ErrorTypes.Vault__ImproperConstantsSetup); } // Updating initial rates in storage rates = liqSupplyExchangePrice_ | (liqBorrowExchangePrice_ << 64) | (EXCHANGE_PRICES_PRECISION << 128) | (EXCHANGE_PRICES_PRECISION << 192); vaultVariables2 = (vaultVariables2 & 0xfffffffffffffffffffffffff800000003ffffffffffffffffffffffffffffff) | (block.timestamp << 122); } fallback() external { if (!(VAULT_FACTORY.isGlobalAuth(msg.sender) || VAULT_FACTORY.isVaultAuth(address(this), msg.sender))) { revert FluidVaultError(ErrorTypes.Vault__NotAnAuth); } // Delegate the current call to `implementation`. // This does not return to its internall call site, it will return directly to the external caller. // solhint-disable-next-line no-inline-assembly _spell(ADMIN_IMPLEMENTATION, msg.data); } receive() external payable {} function _spell(address target_, bytes memory data_) internal returns (bytes memory response_) { assembly { let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0) let size := returndatasize() response_ := mload(0x40) mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f)))) mstore(response_, size) returndatacopy(add(response_, 0x20), 0, size) switch iszero(succeeded) case 1 { // throw if delegatecall failed returndatacopy(0x00, 0x00, size) revert(0x00, size) } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; abstract contract Structs { // structs are used to mitigate Stack too deep errors struct OperateMemoryVars { // ## User's position before update ## uint oldColRaw; uint oldNetDebtRaw; // total debt - dust debt int oldTick; // ## User's position after update ## uint colRaw; uint debtRaw; uint dustDebtRaw; int tick; uint tickId; // others uint256 vaultVariables2; uint256 branchId; int256 topTick; uint liquidityExPrice; uint supplyExPrice; uint borrowExPrice; uint branchData; // user's supply slot data in liquidity uint userSupplyLiquidityData; } struct BranchData { uint id; uint data; uint ratio; uint debtFactor; int minimaTick; uint baseBranchData; } struct TickData { int tick; uint data; uint ratio; uint ratioOneLess; uint length; uint currentRatio; // current tick is ratio with partials. uint partials; } // note: All the below token amounts are in raw form. struct CurrentLiquidity { uint256 debtRemaining; // Debt remaining to liquidate uint256 debt; // Current liquidatable debt before reaching next check point uint256 col; // Calculate using debt & ratioCurrent uint256 colPerDebt; // How much collateral to liquidate per unit of Debt uint256 totalDebtLiq; // Total debt liquidated till now uint256 totalColLiq; // Total collateral liquidated till now int tick; // Current tick to liquidate uint ratio; // Current ratio to liquidate uint tickStatus; // if 1 then it's a perfect tick, if 2 that means it's a liquidated tick int refTick; // ref tick to liquidate uint refRatio; // ratio at ref tick 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 } struct TickHasDebt { int tick; // current tick int nextTick; // next tick with liquidity int mapId; // mapping ID of tickHasDebt uint bitsToRemove; // liquidity to remove till tick_ so we can search for next tick uint tickHasDebt; // getting tickHasDebt_ from tickHasDebt[mapId_] uint mostSigBit; // most significant bit in tickHasDebt_ to get the next tick } struct LiquidateMemoryVars { uint256 vaultVariables2; int liquidationTick; int maxTick; uint256 supplyExPrice; uint256 borrowExPrice; uint256 actualDebtAmt; uint256 actualColAmt; } struct AbsorbMemoryVariables { uint256 debtAbsorbed; uint256 colAbsorbed; int256 startingTick; uint256 mostSigBit; } struct Tokens { address token0; address token1; } struct ConstantViews { address liquidity; address factory; address operateImplementation; address adminImplementation; address secondaryImplementation; address deployer; // address which deploys oracle address supply; // either liquidity layer or DEX protocol address borrow; // either liquidity layer or DEX protocol Tokens supplyToken; // if smart collateral then address of token0 & token1 else just supply token address at token0 and token1 as empty Tokens borrowToken; // if smart debt then address of token0 & token1 else just borrow token address at token0 and token1 as empty uint256 vaultId; uint256 vaultType; bytes32 supplyExchangePriceSlot; // if smart collateral then slot is from DEX protocol else from liquidity layer bytes32 borrowExchangePriceSlot; // if smart debt then slot is from DEX protocol else from liquidity layer bytes32 userSupplySlot; // if smart collateral then slot is from DEX protocol else from liquidity layer bytes32 userBorrowSlot; // if smart debt then slot is from DEX protocol else from liquidity layer } struct RebalanceMemoryVariables { uint256 liqSupplyExPrice; uint256 liqBorrowExPrice; uint256 vaultSupplyExPrice; uint256 vaultBorrowExPrice; uint256 totalSupply; uint256 totalBorrow; uint256 totalSupplyVault; uint256 totalBorrowVault; uint256 initialEth; } }
{ "optimizer": { "enabled": true, "runs": 10000000 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"address","name":"liquidity","type":"address"},{"internalType":"address","name":"factory","type":"address"},{"internalType":"address","name":"operateImplementation","type":"address"},{"internalType":"address","name":"adminImplementation","type":"address"},{"internalType":"address","name":"secondaryImplementation","type":"address"},{"internalType":"address","name":"deployer","type":"address"},{"internalType":"address","name":"supply","type":"address"},{"internalType":"address","name":"borrow","type":"address"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"internalType":"struct Structs.Tokens","name":"supplyToken","type":"tuple"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"internalType":"struct Structs.Tokens","name":"borrowToken","type":"tuple"},{"internalType":"uint256","name":"vaultId","type":"uint256"},{"internalType":"uint256","name":"vaultType","type":"uint256"},{"internalType":"bytes32","name":"supplyExchangePriceSlot","type":"bytes32"},{"internalType":"bytes32","name":"borrowExchangePriceSlot","type":"bytes32"},{"internalType":"bytes32","name":"userSupplySlot","type":"bytes32"},{"internalType":"bytes32","name":"userBorrowSlot","type":"bytes32"}],"internalType":"struct Structs.ConstantViews","name":"constants_","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"colLiquidated","type":"uint256"},{"internalType":"uint256","name":"debtLiquidated","type":"uint256"}],"name":"FluidLiquidateResult","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidLiquidityCalcsError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSafeTransferError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidVaultError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"colAbsorbedRaw_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtAbsorbedRaw_","type":"uint256"}],"name":"LogAbsorb","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator_","type":"address"},{"indexed":false,"internalType":"uint256","name":"colAmt_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtAmt_","type":"uint256"},{"indexed":false,"internalType":"address","name":"to_","type":"address"}],"name":"LogLiquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user_","type":"address"},{"indexed":false,"internalType":"uint256","name":"nftId_","type":"uint256"},{"indexed":false,"internalType":"int256","name":"colAmt_","type":"int256"},{"indexed":false,"internalType":"int256","name":"debtAmt_","type":"int256"},{"indexed":false,"internalType":"address","name":"to_","type":"address"}],"name":"LogOperate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"colAmt_","type":"int256"},{"indexed":false,"internalType":"int256","name":"debtAmt_","type":"int256"}],"name":"LogRebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"supplyExPrice_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowExPrice_","type":"uint256"}],"name":"LogUpdateExchangePrice","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"LIQUIDITY","outputs":[{"internalType":"contract IFluidLiquidity","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TYPE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_FACTORY","outputs":[{"internalType":"contract IFluidVaultFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"constantsView","outputs":[{"components":[{"internalType":"address","name":"liquidity","type":"address"},{"internalType":"address","name":"factory","type":"address"},{"internalType":"address","name":"operateImplementation","type":"address"},{"internalType":"address","name":"adminImplementation","type":"address"},{"internalType":"address","name":"secondaryImplementation","type":"address"},{"internalType":"address","name":"deployer","type":"address"},{"internalType":"address","name":"supply","type":"address"},{"internalType":"address","name":"borrow","type":"address"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"internalType":"struct Structs.Tokens","name":"supplyToken","type":"tuple"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"internalType":"struct Structs.Tokens","name":"borrowToken","type":"tuple"},{"internalType":"uint256","name":"vaultId","type":"uint256"},{"internalType":"uint256","name":"vaultType","type":"uint256"},{"internalType":"bytes32","name":"supplyExchangePriceSlot","type":"bytes32"},{"internalType":"bytes32","name":"borrowExchangePriceSlot","type":"bytes32"},{"internalType":"bytes32","name":"userSupplySlot","type":"bytes32"},{"internalType":"bytes32","name":"userBorrowSlot","type":"bytes32"}],"internalType":"struct Structs.ConstantViews","name":"constantsView_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"dexCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"positionTick_","type":"int256"},{"internalType":"uint256","name":"positionTickId_","type":"uint256"},{"internalType":"uint256","name":"positionRawDebt_","type":"uint256"},{"internalType":"uint256","name":"tickData_","type":"uint256"}],"name":"fetchLatestPosition","outputs":[{"internalType":"int256","name":"","type":"int256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"positionRawCol_","type":"uint256"},{"internalType":"uint256","name":"branchId_","type":"uint256"},{"internalType":"uint256","name":"branchData_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0DebtAmt_","type":"uint256"},{"internalType":"uint256","name":"token1DebtAmt_","type":"uint256"},{"internalType":"uint256","name":"debtSharesMin_","type":"uint256"},{"internalType":"uint256","name":"colPerUnitDebt_","type":"uint256"},{"internalType":"uint256","name":"token0ColAmtPerUnitShares_","type":"uint256"},{"internalType":"uint256","name":"token1ColAmtPerUnitShares_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"bool","name":"absorb_","type":"bool"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"actualDebtShares_","type":"uint256"},{"internalType":"uint256","name":"actualColShares_","type":"uint256"},{"internalType":"uint256","name":"token0Col_","type":"uint256"},{"internalType":"uint256","name":"token1Col_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"debtShares_","type":"uint256"},{"internalType":"uint256","name":"token0DebtAmtPerUnitShares_","type":"uint256"},{"internalType":"uint256","name":"token1DebtAmtPerUnitShares_","type":"uint256"},{"internalType":"uint256","name":"colPerUnitDebt_","type":"uint256"},{"internalType":"uint256","name":"token0ColAmtPerUnitShares_","type":"uint256"},{"internalType":"uint256","name":"token1ColAmtPerUnitShares_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"bool","name":"absorb_","type":"bool"}],"name":"liquidatePerfect","outputs":[{"internalType":"uint256","name":"actualDebtShares_","type":"uint256"},{"internalType":"uint256","name":"token0Debt_","type":"uint256"},{"internalType":"uint256","name":"token1Debt_","type":"uint256"},{"internalType":"uint256","name":"actualColShares_","type":"uint256"},{"internalType":"uint256","name":"token0Col_","type":"uint256"},{"internalType":"uint256","name":"token1Col_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"liquidityCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId_","type":"uint256"},{"internalType":"int256","name":"newColToken0_","type":"int256"},{"internalType":"int256","name":"newColToken1_","type":"int256"},{"internalType":"int256","name":"colSharesMinMax_","type":"int256"},{"internalType":"int256","name":"newDebtToken0_","type":"int256"},{"internalType":"int256","name":"newDebtToken1_","type":"int256"},{"internalType":"int256","name":"debtSharesMinMax_","type":"int256"},{"internalType":"address","name":"to_","type":"address"}],"name":"operate","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"int256","name":"","type":"int256"},{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId_","type":"uint256"},{"internalType":"int256","name":"perfectColShares_","type":"int256"},{"internalType":"int256","name":"colToken0MinMax_","type":"int256"},{"internalType":"int256","name":"colToken1MinMax_","type":"int256"},{"internalType":"int256","name":"perfectDebtShares_","type":"int256"},{"internalType":"int256","name":"debtToken0MinMax_","type":"int256"},{"internalType":"int256","name":"debtToken1MinMax_","type":"int256"},{"internalType":"address","name":"to_","type":"address"}],"name":"operatePerfect","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"int256[]","name":"r_","type":"int256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"slot_","type":"bytes32"}],"name":"readFromStorage","outputs":[{"internalType":"uint256","name":"result_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"},{"internalType":"int256","name":"","type":"int256"},{"internalType":"int256","name":"","type":"int256"},{"internalType":"int256","name":"","type":"int256"}],"name":"rebalance","outputs":[{"internalType":"int256","name":"supplyAmt_","type":"int256"},{"internalType":"int256","name":"borrowAmt_","type":"int256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"debtAmt_","type":"uint256"},{"internalType":"bool","name":"absorb_","type":"bool"}],"name":"simulateLiquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultVariables2_","type":"uint256"}],"name":"updateExchangePrices","outputs":[{"internalType":"uint256","name":"liqSupplyExPrice_","type":"uint256"},{"internalType":"uint256","name":"liqBorrowExPrice_","type":"uint256"},{"internalType":"uint256","name":"vaultSupplyExPrice_","type":"uint256"},{"internalType":"uint256","name":"vaultBorrowExPrice_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateExchangePricesOnStorage","outputs":[{"internalType":"uint256","name":"liqSupplyExPrice_","type":"uint256"},{"internalType":"uint256","name":"liqBorrowExPrice_","type":"uint256"},{"internalType":"uint256","name":"vaultSupplyExPrice_","type":"uint256"},{"internalType":"uint256","name":"vaultBorrowExPrice_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6103006040523480156200001257600080fd5b50604051620062e2380380620062e2833981016040819052620000359162000594565b808080808080610160015161026081815250506127106102605114158015620000635750614e206102605114155b80156200007557506175306102605114155b8015620000875750619c406102605114155b15620000af576040516330090e6560e11b815261793a60048201526024015b60405180910390fd5b80516001600160a01b0390811661020052602082015181166102205260a0820151811660c0908152820151811660e090815282015181166101005261014082015161024052604082015116156200010b5780604001516200010d565b305b6001600160a01b03166101a05261026051614e201480620001325750619c4061026051145b156200014f5760c08101516001600160a01b031660805262000193565b610100810151516001600160a01b03908116608052815160c0830151821691161462000193576040516330090e6560e11b815261793a6004820152602401620000a6565b617530610260511480620001ab5750619c4061026051145b15620001c85760e08101516001600160a01b031660a0526200020c565b610120810151516001600160a01b0390811660a052815160e083015182169116146200020c576040516330090e6560e11b815261793a6004820152602401620000a6565b61010081018051516001600160a01b03908116610120908152830180515182166101609081529251602090810151831661014052905101518116610180908152830151610280526101a08301516102a0526101c0808401516102c0526101e0808501516102e0526060850151831690915260809093015116909152600080546610000000400000178155600b80546001600160a01b03191661dead17905590840151909250614e2014905080620002c95750619c40826101600151145b62000359576001600160401b03605b60e0516001600160a01b031663b5c736e4610280516040518263ffffffff1660e01b81526004016200030c91815260200190565b602060405180830381865afa1580156200032a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003509190620006c3565b901c1662000360565b64e8d4a510005b905060006175308361016001511480620003805750619c40836101600151145b62000411576001600160401b03609b610100516001600160a01b031663b5c736e46102a0516040518263ffffffff1660e01b8152600401620003c491815260200190565b602060405180830381865afa158015620003e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004089190620006c3565b901c1662000418565b64e8d4a510005b905064e8d4a5100082108062000432575064e8d4a5100081105b1562000456576040516330090e6560e11b81526179246004820152602401620000a6565b60408301516001600160a01b031662000487576040516330090e6560e11b815261793a6004820152602401620000a6565b60401b177ce8d4a51000000000e8d4a51000000000000000000000000000000000001760085550506001805442607a1b6401ffffffff607a1b1990911617905550620006dd565b60405161020081016001600160401b03811182821017156200050057634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200051e57600080fd5b919050565b6000604082840312156200053657600080fd5b604080519081016001600160401b03811182821017156200056757634e487b7160e01b600052604160045260246000fd5b604052905080620005788362000506565b8152620005886020840162000506565b60208201525092915050565b60006102408284031215620005a857600080fd5b620005b2620004ce565b620005bd8362000506565b8152620005cd6020840162000506565b6020820152620005e06040840162000506565b6040820152620005f36060840162000506565b6060820152620006066080840162000506565b60808201526200061960a0840162000506565b60a08201526200062c60c0840162000506565b60c08201526200063f60e0840162000506565b60e0820152610100620006558582860162000523565b908201526101406200066a8585830162000523565b61012083015261018080850151828401526101a09150818501516101608401526101c080860151828501526101e09150818601518385015261020086015181850152506102208501518184015250508091505092915050565b600060208284031215620006d657600080fd5b5051919050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e0516159e0620009026000396000611b2c01526000611b05015260008181610b810152611ade01526000818161090f0152611ab70152600081816105fd015281816107e90152818161081401528181610a6101528181610a8c01528181611a900152818161387e015281816138a901528181613a340152613a5f01526000818161050c0152611a690152600081816101540152818161020e0152818161039c01526118a50152600081816104780152818161093901528181610bab015281816116ae015281816116ef0152818161179f015281816118800152818161393e0152613a86015260008181610d3801528181611921015261274a0152600081816102d201526118f9015260008181611503015281816118d10152611bfc015260008181611a42015281816122cc0152818161413701526143fc015260008181611a1c01528181612290015281816140fb01526142a8015260006119ee015260006119c80152600081816115ee0152818161199901528181612229015281816140940152818161424101526143950152600081816115af0152818161197101528181613d6c01528181613e4f0152613f3001526000818161194901526125170152600081816138f1015261397b01526000613ac201526159e06000f3fe6080604052600436106101125760003560e01c80634163f0fa116100a5578063ad20750111610074578063b7791bf211610059578063b7791bf2146105c9578063bb24fe8a146105eb578063cc31808e1461061f57610119565b8063ad2075011461058a578063b5c736e4146105aa57610119565b80634163f0fa146104ba578063540acabc146104fa57806358cc871e1461053c5780639410ae881461056a57610119565b806322348cc7116100e157806322348cc71461040b57806327fa2b53146104535780632861c7d1146104665780633202937e1461049a57610119565b8063021618871461033057806309f0d8cb1461036a578063103f29071461038a5780631593a34b146103e357610119565b3661011957005b34801561012557600080fd5b506040517f4502d0630000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690634502d06390602401602060405180830381865afa1580156101b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d491906151bf565b8061028e57506040517fe04c8e5d0000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e04c8e5d90604401602060405180830381865afa15801561026a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061028e91906151bf565b6102cd576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792560048201526024015b60405180910390fd5b61032e7f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061064092505050565b005b34801561033c57600080fd5b5061034561068c565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b34801561037657600080fd5b506103456103853660046151dc565b6107ad565b34801561039657600080fd5b506103be7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610361565b6103f66103f13660046151f5565b610c91565b60408051928352602083019190915201610361565b34801561041757600080fd5b5061042b6104263660046151f5565b610dc5565b604080519586526020860194909452928401919091526060830152608082015260a001610361565b610345610461366004615249565b611085565b34801561047257600080fd5b506103be7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104a657600080fd5b5061032e6104b53660046152bc565b611268565b6104cd6104c8366004615249565b6112c3565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610361565b34801561050657600080fd5b5061052e7f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610361565b61054f61054a3660046152ec565b61145a565b60408051938452602084019290925290820152606001610361565b34801561057657600080fd5b5061032e610585366004615345565b611597565b34801561059657600080fd5b5061032e6105a5366004615371565b6116d7565b3480156105b657600080fd5b5061052e6105c53660046151dc565b5490565b3480156105d557600080fd5b506105de6117ca565b60405161036191906153fa565b3480156105f757600080fd5b5061052e7f000000000000000000000000000000000000000000000000000000000000000081565b61063261062d3660046152ec565b611b54565b6040516103619291906155f2565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e81156001810361068357816000803e816000fd5b50505092915050565b60008060008061069d6001546107ad565b9296509094509250905067ffffffffffffffff8411806106c4575067ffffffffffffffff83115b806106d6575067ffffffffffffffff82115b806106e8575067ffffffffffffffff81115b15610723576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792d60048201526024016102c4565b604083811b8517608084901b1760c083901b17600855600180547ffffffffffffffffffffffffff800000003ffffffffffffffffffffffffffffff1642607a1b179055517fcde545703e0372175cadfff811d67c32910c3dcb33199679b3271c4106afdf9a9061079f9084908490918252602082015260400190565b60405180910390a190919293565b60085460009081908190819067ffffffffffffffff80821690604083901c16836107e2607a8a901c6401ffffffff164261566f565b9050614e207f000000000000000000000000000000000000000000000000000000000000000014806108355750619c407f0000000000000000000000000000000000000000000000000000000000000000145b156108e75764e8d4a51000975067ffffffffffffffff608085901c169550886001166001036108a65761086e6301e13380612710615682565b60018a901c617fff166108818389615682565b61088b9190615682565b61089591906156c8565b61089f9087615703565b9550610a5c565b6108b66301e13380612710615682565b60018a901c617fff166108c98389615682565b6108d39190615682565b6108dd91906156c8565b61089f908761566f565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201526109bf907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024015b602060405180830381865afa158015610996573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ba9190615716565b611c8f565b50975082881015610a00576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793260048201526024016102c4565b61271061ffff8a16670de0b6b3a7640000858b670de0b6b3a76400000281610a2a57610a2a615699565b04030281610a3a57610a3a615699565b670de0b6b3a7640000919004810167ffffffffffffffff608087901c16020495505b6175307f00000000000000000000000000000000000000000000000000000000000000001480610aad5750619c407f0000000000000000000000000000000000000000000000000000000000000000145b15610b595764e8d4a51000965060c084901c9450600160108a901c81169003610b1857610ae06301e13380612710615682565b60118a901c617fff16610af38388615682565b610afd9190615682565b610b0791906156c8565b610b119086615703565b9450610c86565b610b286301e13380612710615682565b60118a901c617fff16610b3b8388615682565b610b459190615682565b610b4f91906156c8565b610b11908661566f565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006004820152610bef907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401610979565b97505081871015610c30576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793260048201526024016102c4565b61271061ffff60108b901c16670de0b6b3a7640000848a670de0b6b3a76400000281610c5e57610c5e615699565b04030281610c6e57610c6e615699565b670de0b6b3a7640000919004810160c086901c020494505b505050509193509193565b600b54600090819073ffffffffffffffffffffffffffffffffffffffff1661dead14610ced576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055604080516020601f36908101829004820283018201909352828252610d79927f00000000000000000000000000000000000000000000000000000000000000009291600091908190840183828082843760009201919091525061064092505050565b806020019051810190610d8c919061572f565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790559097909650945050505050565b600080808080868180600189901c62ffffff168b9003610e0b575050601a87901c633fffffff169250603887901c6603ffffffffffff166001601989901c811614610e62565b60008c8152600660205260408120600360028e0181810660550292918491048152602081019190915260400160002054901c600181811c633fffffff169750601f82901c6603ffffffffffff169450908116149150505b60008581526007602052604090205493508015610ea5577f80000000000000000000000000000000000000000000000000000000000000009b5060009950611071565b83600316600203610f0f57610ec782607486901c6603ffffffffffff16611e9d565b91506001610ed7600f6023615703565b6001901b610ee5919061566f565b8214610f0f5760a69390931c633fffffff1660008181526007602052604090205490945092610ea5565b8360031660031480610f3b57506001610f2a600f6023615703565b6001901b610f38919061566f565b82145b15610f6c577f80000000000000000000000000000000000000000000000000000000000000009b5060009950611071565b610f848a607486901c6603ffffffffffff1684611f37565b9950606483048a1115610fa15761271061270f8b02049950610fa6565b600099505b891561104d5783600416600414610fc9576207ffff600385901c16600003610fd4565b6207ffff600385901c165b9b506000610fe48d60020b611f7c565b905061271f612710820204633fffffff601687901c8116611005838561566f565b61100f9190615682565b61101991906156c8565b6110239082615703565b61103a6c010000000000000000000000008e615682565b61104491906156c8565b97505050611071565b7f80000000000000000000000000000000000000000000000000000000000000009b505b8b8a97509750505050945094509450945094565b600b5460009081908190819073ffffffffffffffffffffffffffffffffffffffff1661dead146110e5576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055600080549060018216900361112a5760018117600055611160565b6040517f60121cca00000000000000000000000000000000000000000000000000000000815261791960048201526024016102c4565b600061116c344761566f565b905073ffffffffffffffffffffffffffffffffffffffff8816156111905787611192565b335b975060006111a18f8f8f6121de565b90506111b0818d8b8b876123c0565b8060200190518101906111c39190615753565b9198509650925080871015611208576040517f60121cca0000000000000000000000000000000000000000000000000000000081526188ba60048201526024016102c4565b611214868c8c8c613cb9565b6000859055909550935061122782613fe5565b5050600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905550929b919a509850909650945050505050565b600080549060018216900361112a5760018117600055821561128a578261129c565b6fffffffffffffffffffffffffffffffff5b92506112be6fffffffffffffffffffffffffffffffff600061dead85856123c0565b600080fd5b600b546000908190819081908190819073ffffffffffffffffffffffffffffffffffffffff1661dead14611327576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055604080518082019091526000808252602082015260008054808352600116900361112a578051600117600055611387344761566f565b602082015273ffffffffffffffffffffffffffffffffffffffff8916156113ae57886113b0565b335b98506113c38f8d8b8b85600001516123c0565b8060200190518101906113d69190615753565b835290975093508e15611405576113ee878f8f614039565b90965094506113ff848c8c8c613cb9565b90935091505b8051600055602081015161141890613fe5565b50600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055949d939c50919a509850965090945092505050565b600b546000908190819073ffffffffffffffffffffffffffffffffffffffff1661dead146114b8576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055604080516020601f36908101829004820283018201909352828252611544927f00000000000000000000000000000000000000000000000000000000000000009291600091908190840183828082843760009201919091525061064092505050565b8060200190518101906115579190615753565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055919d909c50909a5098505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016148061161057503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b61164a576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793c60048201526024016102c4565b60005460011660000361168d576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792a60048201526024016102c4565b600b546116d390839073ffffffffffffffffffffffffffffffffffffffff167f0000000000000000000000000000000000000000000000000000000000000000846144da565b5050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461174a576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792960048201526024016102c4565b60005460011660000361178d576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792a60048201526024016102c4565b6117c48461179d83850185615781565b7f0000000000000000000000000000000000000000000000000000000000000000866144da565b50505050565b611869604080516102008101825260008082526020808301829052828401829052606083018290526080830182905260a0830182905260c0830182905260e0830182905283518085018552828152808201839052610100840152835180850190945281845283015290610120820190815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811682527f000000000000000000000000000000000000000000000000000000000000000081166020808401919091527f0000000000000000000000000000000000000000000000000000000000000000821660408401527f0000000000000000000000000000000000000000000000000000000000000000821660608401527f0000000000000000000000000000000000000000000000000000000000000000821660808401527f0000000000000000000000000000000000000000000000000000000000000000821660a08401527f0000000000000000000000000000000000000000000000000000000000000000821660c08401527f0000000000000000000000000000000000000000000000000000000000000000821660e0840152610100830180517f000000000000000000000000000000000000000000000000000000000000000084169052517f0000000000000000000000000000000000000000000000000000000000000000831690820152610120830180517f000000000000000000000000000000000000000000000000000000000000000084169052517f00000000000000000000000000000000000000000000000000000000000000009092169101527f00000000000000000000000000000000000000000000000000000000000000006101408201527f00000000000000000000000000000000000000000000000000000000000000006101608201527f00000000000000000000000000000000000000000000000000000000000000006101808201527f00000000000000000000000000000000000000000000000000000000000000006101a08201527f00000000000000000000000000000000000000000000000000000000000000006101c08201527f00000000000000000000000000000000000000000000000000000000000000006101e082015290565b600b5460009060609073ffffffffffffffffffffffffffffffffffffffff1661dead14611bb1576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055604080516020601f36908101829004820283018201909352828252611c3d927f00000000000000000000000000000000000000000000000000000000000000009291600091908190840183828082843760009201919091525061064092505050565b806020019051810190611c5091906157cd565b91509150600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905590999098509650505050505050565b67ffffffffffffffff605b82901c811690609b83901c16811580611cb1575080155b15611ced576040517fd50d75120000000000000000000000000000000000000000000000000000000081526201117160048201526024016102c4565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480611d17575082155b80611d225750806001145b15611d2f57505050915091565b64496cebb80084840283020484019350617fff60db87901c16925082600103611d5a57505050915091565b82600116600103611daf5760019290921c91826c7e37be2022c0914b268000000081611d8857611d88615699565b049250612710601e87901c613fff166b033b2e3c9fd0803ce8000000850102049250611ddc565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b80600116600103611e135760011c61271081016b033b2e3c9fd0803ce8000000820281611e0b57611e0b615699565b049050611e49565b60011c61271081016b033b2e3c9fd0803ce8000000820281611e3757611e37615699565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b6000600f83811c9083901c0281681fffffffffffffffff8211611ec1576022611ec4565b60235b91821c919050617fff858116908516018101614000811015611ee557600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001617fff811115611f2457506603ffffffffffff9250611f31915050565b80600f84901b1793505050505b92915050565b6000617fff838116908316036081811015611f6f57600f83811c821b9085901c860281611f6657611f66615699565b04915050611f75565b60009150505b9392505050565b600060ff82901d80831803617fff811115611f9657600080fd5b7001000000000000000000000000000000006001821615611fc457506fff9dd7de423466c20352b1246ce4856f5b6002821615611fe3576fff3bd55f4488ad277531fa1c725a66d00260801c5b6004821615612002576ffe78410fd6498b73cb96a6917f8532590260801c5b6008821615612021576ffcf2d9987c9be178ad5bfeffaa1232730260801c5b6010821615612040576ff9ef02c4529258b057769680fc6601b30260801c5b602082161561205f576ff402d288133a85a17784a411f7aba0820260801c5b604082161561207e576fe895615b5beb6386553757b0352bda900260801c5b608082161561209d576fd34f17a00ffa00a8309940a15930391a0260801c5b6101008216156120bd576fae6b7961714e20548d88ea5123f9a0ff0260801c5b6102008216156120dd576f76d6461f27082d74e0feed3b388c0ca10260801c5b6104008216156120fd576f372a3bfe0745d8b6b19d985d9a8b85bb0260801c5b61080082161561211d576f0be32cbee48979763cf7247dd7bb539d0260801c5b61100082161561213c576e8d4f70c9ff4924dac37612d1e2921e0260801c5b61200082161561215a576d4e009ae5519380809a02ca7aec770260801c5b614000821615612176576b17c45e641b6e95dee056ff100260801c5b600091507f800000000000000000000000000000000000000000000000000000000000000084166121d4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0463ffffffff8116156121d457600191505b60201c0192915050565b6000831580156121ec575082155b15612227576040517f60121cca0000000000000000000000000000000000000000000000000000000081526188b960048201526024016102c4565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16636876698173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1614612328577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461232257600061232a565b8461232a565b855b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810188905260248101879052604481018690526000606482015260840160206040518083038185885af1158015612393573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906123b89190615716565b949350505050565b60606124026040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001548152600283901c620fffff1660000361244e576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791f60048201526024016102c4565b8051612459906107ad565b909192509091508260600183608001828152508281525050506124d66040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b61250f6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60008061254a7f0000000000000000000000000000000000000000000000000000000000000000633fffffff605c8860000151901c166145a1565b73ffffffffffffffffffffffffffffffffffffffff1663f3190c896040518163ffffffff1660e01b8152600401602060405180830381865afa158015612594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b89190615716565b9150760a70c3c40a64e6c51999090b65f67d92400000000000008211806125dd575081155b15612618576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793960048201526024016102c4565b8460800151856060015183028161263157612631615699565b049150722cd76fe086b93ce2f768a00b22a0000000000082111561266657722cd76fe086b93ce2f768a00b22a0000000000091505b81760a70c3c40a64e6c51999090b65f67d92400000000000008161268c5761268c615699565b865191900491506127109060481c6103ff16810182020460608501526b033b2e3c9fd0803ce80000006c010000000000000000000000008302865191900492506103e890602a1c6103ff1683020490506126e581614a0b565b50602086015284516103e89060341c6103ff16830204905061270681614a0b565b506040860152600480881614612728576207ffff600388901c16600003612733565b6207ffff600388901c165b60c0850181905260408601511215612888576128047f0000000000000000000000000000000000000000000000000000000000000000888760400151604051602401612789929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9e3e482100000000000000000000000000000000000000000000000000000000179052610640565b8060200190518101906128179190615716565b965086600416600414612836576207ffff600388901c16600003612841565b6207ffff600388901c165b60c085015260008b90036128885760408051600060208201819052918101919091526060810188905260800160405160208183030381529060405295505050505050613cb0565b6127108b10806128a757506fffffffffffffffffffffffffffffffff8b115b156128e2576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792e60048201526024016102c4565b60028716156128f25760026128f5565b60015b60ff16846101000181815250506129426040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60c08501518152601688901c633fffffff16808552600090815260076020908152604082205490860181905260741c6603ffffffffffff16606086018190529003612995576603ffffffffc00060608501525b602084015160c41c620fffff16925082156129dc57826001166001146129c7576207ffff600184901c166000036129d2565b6207ffff600184901c165b6080850152612a03565b7f800000000000000000000000000000000000000000000000000000000000000060808501525b856080015164e8d4a510008d0281612a1d57612a1d615699565b0480865266ffffffffffffff609a8a901c1660ff60928b901c161b9250612a48633b9aca00846156c8565b1115612a84576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792e60048201526024016102c4565b8815612b175760025485516fffffffffffffffffffffffffffffffff8216945060809190911c9250831115612afc5784518390612ac19084615682565b612acb91906156c8565b60a08601819052612adc908361566f565b855160808088018290526000885282901b94039384176002559150612b17565b6000600255845183900385526080850183905260a085018290525b85602001518560c0015113156136c4578451156136c4576040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081018290528190680100000000000000009060008960c0015112612b93576101008960c0015181612b8d57612b8d615699565b05612bb0565b60016101008a60c0015160010181612bad57612bad615699565b05035b60408201528451612bc090611f7c565b6040860152610100890151600103612beb57604085015160e08a015260c08901516020820152612d10565b61271f85604001516127100281612c0457612c04615699565b046060860181905260408601510360808601819052602089015160161c633fffffff90811660c0880181905290910260608701519190040160e08a0152845160208b0151600101148015612c5c57508460c001516001145b15612d10577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff215373ffffffffffffffffffffffffffffffffffffffff8f1601612cda576040517fcc36aa4400000000000000000000000000000000000000000000000000000000815260006004820181905260248201526044016102c4565b6040517f60121cca00000000000000000000000000000000000000000000000000000000815261793560048201526024016102c4565b886101000151600103612d805760c0890180516000908152600560205260408082205460608c01518c5194518452919092206301fffffe8316601a9490941b9390931760389190911b1760011790915566ffffffffffffff602182901c1660ff601983901c161b97509550612da3565b67ffffffffffffffff60348960200151901c16965060ff8716600888901c901b96505b8689602001818151612db59190615703565b90525060e0890151612dd46c0100000000000000000000000089615682565b612dde91906156c8565b89604001818151612def9190615703565b90525060c08901516020820151148015612e0e57508861010001516001145b80612e1b57506080810151155b15612f4b578060800151600003612e445760408082015160009081526004602052205460808201525b886101000151600103612e9b57604081015160c08a015160009081036101009283020190910160608301819052608083018051821b90911c908190529003612e9b5760408082015160009081526004602052908120555b608081015115612ec9576001612eb48260800151614e20565b60408301516101000201036020820152612f4b565b89602001518160400151610100021215612f08577f80000000000000000000000000000000000000000000000000000000000000006020820152612f4b565b604080820180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01908190526000908152600460205220546080820152612e9b565b80602001518860800151138015612f69575089602001518860800151135b15612f865760808801516101208a015260026101608a0152612fc2565b896020015181602001511315612fae5760208101516101208a015260016101608a0152612fc2565b60208a01516101208a015260036101608a01525b612fd389610120015160020b611f7c565b60408601526101608901516002036130675761271f85604001516127100281612ffe57612ffe615699565b046060860181905260408087015191909103608087019081526020808b015160a61c633fffffff908116600090815260079092529290205460a08b0181905260161c821660c0880181905290510260608701519190040160a086018190526101408a0152613084565b604085015160a086018190526101408a0152633fffffff60c08601525b6c010000000000000000000000008961014001518a606001516130a79190615682565b6130b191906156c8565b6130c7906b033b2e3c9fd0803ce800000061566f565b8960e001518a602001518b61014001516130e19190615682565b6130eb91906156c8565b8a602001516130fa919061566f565b613110906b033b2e3c9fd0803ce8000000615682565b61311a91906156c8565b93506b033b2e3c9fd0803ce80000008960600151856131399190615682565b61314391906156c8565b92508389602001510361315e5761315b60018561566f565b93505b88518410158061317357508861016001516003145b156134de5760808101516040808301516000908152600460205220558851841061335357885160608a01519094506b033b2e3c9fd0803ce8000000906131b99086615682565b6131c391906156c8565b92508289604001516131d5919061566f565b6c01000000000000000000000000858b602001516131f3919061566f565b6131fd9190615682565b61320791906156c8565b965061321287614a0b565b60608701528086526101208a01511380156132345750633fffffff8560c00151145b1561324c5784516002018552600160c0860152613366565b84516001018552610160890151600214801561326d57506101208901518551145b61327857600061327e565b8460c001515b9550612710856060015161271f028161329957613299615699565b046040860181905260608601519081900360808701819052908803633fffffff02816132c7576132c7615699565b0460c08601819052156132f857633fffffff8560c0015110156132ee578460c001516132fb565b633ffffffe6132fb565b60015b60c0860152851580159061331357508460c001518610155b1561334e576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793860048201526024016102c4565b613366565b6101208901516001908101865260c08601525b6020890151613375858261566f565b61337f9084615682565b61338991906156c8565b9150838960800181815161339d9190615703565b9052506020890180518591906133b490839061566f565b90525060a0890180518491906133cb908390615703565b9052506040890180518491906133e290839061566f565b90525060608801516133f49083614ed6565b606089015260208901516064111561343c576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793060048201526024016102c4565b8451600013613452578451600190811b1761345b565b845160000360011b5b606089015160208b015191975060741b9060349061347e90603860086001614f89565b901b60168760c00151901b600289901b60a6808d60200151901c901b60011717171717600760008a6000015181526020019081526020016000208190555060168860000151901b600287901b6034808f901c901b60021717179b506136bf565b8851849003895260208901516134f4858261566f565b6134fe9084615682565b61350891906156c8565b9150838960800181815161351c9190615703565b90525060208901805185919061353390839061566f565b90525060a08901805184919061354a908390615703565b90525060408901805184919061356190839061566f565b90525060608801516135739083614ed6565b606089015261016089015168010000000000000000925060020361369857633fffffff60a68960200151901c1696508760a00151955060006603ffffffffffff607488901c16905060006135d48a60600151836150b290919063ffffffff16565b6020808c0180518d516000908152600790935260409092207fffffffffffffffffffffffc00000000000000000000000000000000000000000909216607484901b17600217909155998b5298889052506060890181905260c487901c620fffff1697881561366e5788600116600114613659576207ffff60018a901c16600003613664565b6207ffff60018a901c165b60808b0152613695565b7f800000000000000000000000000000000000000000000000000000000000000060808b01525b50505b61012089015160c08a01526101608901516101008a015261014089015160e08a0152612d10565b505050505b64e8d4a51000866080015186608001516136de9190615682565b6136e891906156c8565b8660a001818152505064e8d4a5100086606001518660a0015161370b9190615682565b61371591906156c8565b60c087015260a08601518c10156137515760a0860151613735908d6156c8565b8660c001516137449190615682565b60c087015260a086018c90525b8560a00151600003613793576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793560048201526024016102c4565b8a8660a001518760c00151670de0b6b3a76400006137b19190615682565b6137bb91906156c8565b10156137f7576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792160048201526024016102c4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff215373ffffffffffffffffffffffffffffffffffffffff8b16016138795760c086015160a08701516040517fcc36aa44000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016102c4565b6175307f000000000000000000000000000000000000000000000000000000000000000014806138ca5750619c407f0000000000000000000000000000000000000000000000000000000000000000145b613a2f577fffffffffffffffffffffffff11111111111111111111111111111111111111127f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1601613937578560a00151925061393c565b600092505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad967e15847f000000000000000000000000000000000000000000000000000000000000000060008a60a001516139aa906158b5565b604080513360208201526000918291016040516020818303038152906040526040518863ffffffff1660e01b81526004016139ea96959493929190615951565b604080518083038185885af1158015613a07573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613a2c919061572f565b50505b614e207f00000000000000000000000000000000000000000000000000000000000000001480613a805750619c407f0000000000000000000000000000000000000000000000000000000000000000145b613b69577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad967e157f00000000000000000000000000000000000000000000000000000000000000008860c00151613aef906158b5565b604080516000808252602082019092528f9082906040518763ffffffff1660e01b8152600401613b2496959493929190615951565b60408051808303816000875af1158015613b42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b66919061572f565b50505b67ffffffffffffffff605289901c1692508460a0015160ff8416600885901c901b613b94919061566f565b925067ffffffffffffffff609289901c169150846080015160ff8316600884901c901b613bc1919061566f565b91506092613bd483603860086001614f89565b901b6052613be785603860086000614f89565b60c089015160a08a015160408051338152602081019390935282015273ffffffffffffffffffffffffffffffffffffffff8e1660608201527ffffffffffffc00000000000000000000000000000003ffffffffffffffffffff909b16911b1717977f80fd9cc6b1821f4a510e45ffce6852ea3404807b5d3d833ffa85664408afcb669060800160405180910390a160a086015160c08701516040805160208101939093528201526060810189905260800160405160208183030381529060405296505050505050505b95945050505050565b60008080670de0b6b3a7640000613cd08888615682565b613cda91906156c8565b90506000670de0b6b3a7640000613cf18988615682565b613cfb91906156c8565b9050600082118015613d0d5750600081115b15613de2576040517f35f0df9800000000000000000000000000000000000000000000000000000000815260048101899052602481018390526044810182905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f000000000000000000000000000000000000000000000000000000000000000016906335f0df989060840160408051808303816000875af1158015613db4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd8919061572f565b9094509250613fda565b600082118015613df0575080155b15613ec3576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101899052602481018390526044810182905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f00000000000000000000000000000000000000000000000000000000000000001690634c89bfd4906084016020604051808303816000875af1158015613e98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ebc9190615716565b9350613fda565b81158015613ed15750600081115b15613fa4576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101899052602481018390526044810182905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f00000000000000000000000000000000000000000000000000000000000000001690634c89bfd4906084016020604051808303816000875af1158015613f79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f9d9190615716565b9250613fda565b6040517f60121cca0000000000000000000000000000000000000000000000000000000081526188b960048201526024016102c4565b505094509492505050565b303181811115613ffb576116d33383830361515d565b818110156116d3576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791b60048201526024016102c4565b60008080670de0b6b3a76400006140508787615682565b61405a91906156c8565b90506000670de0b6b3a76400006140718887615682565b61407b91906156c8565b905060008211801561408d5750600081115b1561422c577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16635b3d38d773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1614614193577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461418d576000614195565b82614195565b835b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b168152600481018b9052602481018690526044810185905260006064820152608401604080518083038185885af11580156141fd573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190614222919061572f565b90945092506144d0565b60008211801561423a575080155b15614380577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166330acd6fd73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16146142e95760006142eb565b835b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b168152600481018b905260248101869052604481018590526000606482015260840160206040518083038185885af1158015614354573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906143799190615716565b93506144d0565b8115801561438e5750600081115b15613fa4577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166330acd6fd73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff161461443d57600061443f565b825b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b168152600481018b905260248101869052604481018590526000606482015260840160206040518083038185885af11580156144a8573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906144cd9190615716565b92505b5050935093915050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061459a576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155960048201526024016102c4565b5050505050565b60006060826000036145b7576000915050611f31565b607f8311614680576040517fd60000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16602282015260f884901b7fff000000000000000000000000000000000000000000000000000000000000001660368201526037015b60405160208183030381529060405290506149fc565b60ff831161475d576040517fd70000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f8100000000000000000000000000000000000000000000000000000000000000603682015260f884901b7fff0000000000000000000000000000000000000000000000000000000000000016603782015260380161466a565b61ffff831161483b576040517fd80000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f820000000000000000000000000000000000000000000000000000000000000060368201527fffff00000000000000000000000000000000000000000000000000000000000060f085901b16603782015260390161466a565b62ffffff831161491a576040517fd90000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f830000000000000000000000000000000000000000000000000000000000000060368201527fffffff000000000000000000000000000000000000000000000000000000000060e885901b166037820152603a0161466a565b6040517fda0000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f840000000000000000000000000000000000000000000000000000000000000060368201527fffffffff0000000000000000000000000000000000000000000000000000000060e085901b166037820152603b0160405160208183030381529060405290505b80516020909101209392505050565b600080630235b88083107473d85bca016a2338b31715f8e13054c005f8b995d384111715614a3857600080fd5b6c010000000000000000000000008310600081614a6e57506c010000000000000000000000006a52b7d2dcc80cd2e40000008502045b8115614a9157507652b7d2dcc80cd2e40000000000000000000000000000008490045b6f037af932b2affa9738cc6c38ca5278318110614ad257614000841793506f037af932b2affa9738cc6c38ca5278316a52b7d2dcc80cd2e400000082020490505b6d010f7a088a76f267264caa114f0a8110614b0f57612000841793506d010f7a088a76f267264caa114f0a6a52b7d2dcc80cd2e400000082020490505b6b95da74f87f839fc2e0dc5bd98110614b4857611000841793506b95da74f87f839fc2e0dc5bd96a52b7d2dcc80cd2e400000082020490505b6b06f55dedafd8491caed5a1b88110614b8157610800841793506b06f55dedafd8491caed5a1b86a52b7d2dcc80cd2e400000082020490505b6b017fdd10ee11e624491b4cc18110614bba57610400841793506b017fdd10ee11e624491b4cc16a52b7d2dcc80cd2e400000082020490505b6ab23131bf0c30217b0a2c698110614bf157610200841793506ab23131bf0c30217b0a2c696a52b7d2dcc80cd2e400000082020490505b6a79683edcb9280d797aded78110614c2857610100841793506a79683edcb9280d797aded76a52b7d2dcc80cd2e400000082020490505b6a64366e2f9919f0d9b0dc908110614c5e576080841793506a64366e2f9919f0d9b0dc906a52b7d2dcc80cd2e400000082020490505b6a5b0bcda5a78850646b0a818110614c94576040841793506a5b0bcda5a78850646b0a816a52b7d2dcc80cd2e400000082020490505b6a56c840f992c70f959ae8108110614cca576020841793506a56c840f992c70f959ae8106a52b7d2dcc80cd2e400000082020490505b6a54b9cd178695194f9be0a08110614d00576010841793506a54b9cd178695194f9be0a06a52b7d2dcc80cd2e400000082020490505b6a53b7458aff204b5e65d6818110614d36576008841793506a53b7458aff204b5e65d6816a52b7d2dcc80cd2e400000082020490505b6a53372a2f38c240d689e4008110614d6c576004841793506a53372a2f38c240d689e4006a52b7d2dcc80cd2e400000082020490505b6a52f76617a04499e66400008110614da2576002841793506a52f76617a04499e66400006a52b7d2dcc80cd2e400000082020490505b6a52d79660f3dec355c000008110614dd8576001841793506a52d79660f3dec355c000006a52b7d2dcc80cd2e400000082020490505b81614df057806a52b7d2dcc80cd2e400000086020492505b8115614e0c579219926a52d79660f3dec355c000008582020492505b505082811115614e1b57600080fd5b915091565b6000816fffffffffffffffffffffffffffffffff831115614e425760809150811c5b67ffffffffffffffff811115614e5a576040918201911c5b63ffffffff811115614e6e576020918201911c5b61ffff811115614e80576010918201911c5b60ff811115614e91576008918201911c5b600f811115614ea2576004918201911c5b6003811115614eb3576002918201911c5b6001811115614ec3576001820191505b8015614ed0576001820191505b50919050565b6000600f83901c8202816c03ffffffffffffffffffffffff8211614f1d576c01ffffffffffffffffffffffff8211614f1657614f1182614e20565b614f20565b6062614f20565b60635b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd617fff87168201810194500191821c91905060408311156112be5750600f1b017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00192915050565b600080856fffffffffffffffffffffffffffffffff811115614fac5760809150811c5b67ffffffffffffffff811115614fc4576040918201911c5b63ffffffff811115614fd8576020918201911c5b61ffff811115614fea576010918201911c5b60ff811115614ffb576008918201911c5b600f81111561500c576004918201911c5b600381111561501d576002918201911c5b600181111561502d576001820191505b801561503a576001820191505b50848110156150465750835b848103905085811c6000821184161561509557600181019050806001871b0361509557506001908101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86011b5b6001851b82106150a457600080fd5b90931b909201949350505050565b600080600f83901c7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000603186901b16816150ee576150ee615699565b0490506000604082901c600114615106576040615109565b60415b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd81019290921c91617fff8581166040019250613fdd90871690910101818111156112be5703600f9190911b179050611f31565b60008060008060008587614e20f19050806151a9576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016102c4565b505050565b80151581146151bc57600080fd5b50565b6000602082840312156151d157600080fd5b8151611f75816151ae565b6000602082840312156151ee57600080fd5b5035919050565b6000806000806080858703121561520b57600080fd5b5050823594602084013594506040840135936060013592509050565b73ffffffffffffffffffffffffffffffffffffffff811681146151bc57600080fd5b600080600080600080600080610100898b03121561526657600080fd5b883597506020890135965060408901359550606089013594506080890135935060a0890135925060c089013561529b81615227565b915060e08901356152ab816151ae565b809150509295985092959890939650565b600080604083850312156152cf57600080fd5b8235915060208301356152e1816151ae565b809150509250929050565b600080600080600080600080610100898b03121561530957600080fd5b883597506020890135965060408901359550606089013594506080890135935060a0890135925060c0890135915060e08901356152ab81615227565b6000806040838503121561535857600080fd5b823561536381615227565b946020939093013593505050565b6000806000806060858703121561538757600080fd5b843561539281615227565b935060208501359250604085013567ffffffffffffffff808211156153b657600080fd5b818701915087601f8301126153ca57600080fd5b8135818111156153d957600080fd5b8860208285010111156153eb57600080fd5b95989497505060200194505050565b815173ffffffffffffffffffffffffffffffffffffffff16815261024081016020830151615440602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151615468604084018273ffffffffffffffffffffffffffffffffffffffff169052565b506060830151615490606084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060808301516154b8608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a08301516154e060a084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c083015161550860c084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060e083015161553060e084018273ffffffffffffffffffffffffffffffffffffffff169052565b5061010083810151805173ffffffffffffffffffffffffffffffffffffffff90811685840152602082015116610120850152505061012083015161014061559c81850183805173ffffffffffffffffffffffffffffffffffffffff908116835260209182015116910152565b840151610180848101919091526101608501516101a080860191909152908501516101c080860191909152908501516101e080860191909152908501516102008501529093015161022090920191909152919050565b6000604082018483526020604081850152818551808452606086019150828701935060005b8181101561563357845183529383019391830191600101615617565b5090979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115611f3157611f31615640565b8082028115828204841417611f3157611f31615640565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826156fe577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b80820180821115611f3157611f31615640565b60006020828403121561572857600080fd5b5051919050565b6000806040838503121561574257600080fd5b505080516020909101519092909150565b60008060006060848603121561576857600080fd5b8351925060208401519150604084015190509250925092565b60006020828403121561579357600080fd5b8135611f7581615227565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080604083850312156157e057600080fd5b8251915060208084015167ffffffffffffffff8082111561580057600080fd5b818601915086601f83011261581457600080fd5b8151818111156158265761582661579e565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f830116810181811085821117156158695761586961579e565b60405291825284820192508381018501918983111561588757600080fd5b938501935b828510156158a55784518452938501939285019261588c565b8096505050505050509250929050565b60007f800000000000000000000000000000000000000000000000000000000000000082036158e6576158e6615640565b5060000390565b6000815180845260005b81811015615913576020818501810151868301820152016158f7565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352876020840152866040840152808616606084015280851660808401525060c060a083015261599e60c08301846158ed565b9897505050505050505056fea26469706673582212202cd8364122fdd123f7e831cb09cc663a8e3a459240ed4213bdb860def13d8fb264736f6c6343000815003300000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d00000000000000000000000097fc217b251b50e42d4d2b99e3fb6a7b67148461000000000000000000000000827b69a0fe822b44286c83d671eda78ee40f138d000000000000000000000000b6ff36e7d04e6017e0323f1f92a8184370d489070000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee45000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee45000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000009c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018b0d2b1c86d0f8d3f949ae13f3c0e8a901b02dc6720e6b1170940b98ba25f2ae18a5692bf658b6b1c214d6f6901b5945c696d883890d12b0528a93d9bccfbb2
Deployed Bytecode
0x6080604052600436106101125760003560e01c80634163f0fa116100a5578063ad20750111610074578063b7791bf211610059578063b7791bf2146105c9578063bb24fe8a146105eb578063cc31808e1461061f57610119565b8063ad2075011461058a578063b5c736e4146105aa57610119565b80634163f0fa146104ba578063540acabc146104fa57806358cc871e1461053c5780639410ae881461056a57610119565b806322348cc7116100e157806322348cc71461040b57806327fa2b53146104535780632861c7d1146104665780633202937e1461049a57610119565b8063021618871461033057806309f0d8cb1461036a578063103f29071461038a5780631593a34b146103e357610119565b3661011957005b34801561012557600080fd5b506040517f4502d0630000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d73ffffffffffffffffffffffffffffffffffffffff1690634502d06390602401602060405180830381865afa1580156101b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d491906151bf565b8061028e57506040517fe04c8e5d0000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d73ffffffffffffffffffffffffffffffffffffffff169063e04c8e5d90604401602060405180830381865afa15801561026a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061028e91906151bf565b6102cd576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792560048201526024015b60405180910390fd5b61032e7f000000000000000000000000827b69a0fe822b44286c83d671eda78ee40f138d6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061064092505050565b005b34801561033c57600080fd5b5061034561068c565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b34801561037657600080fd5b506103456103853660046151dc565b6107ad565b34801561039657600080fd5b506103be7f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d81565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610361565b6103f66103f13660046151f5565b610c91565b60408051928352602083019190915201610361565b34801561041757600080fd5b5061042b6104263660046151f5565b610dc5565b604080519586526020860194909452928401919091526060830152608082015260a001610361565b610345610461366004615249565b611085565b34801561047257600080fd5b506103be7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49781565b3480156104a657600080fd5b5061032e6104b53660046152bc565b611268565b6104cd6104c8366004615249565b6112c3565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610361565b34801561050657600080fd5b5061052e7f000000000000000000000000000000000000000000000000000000000000001681565b604051908152602001610361565b61054f61054a3660046152ec565b61145a565b60408051938452602084019290925290820152606001610361565b34801561057657600080fd5b5061032e610585366004615345565b611597565b34801561059657600080fd5b5061032e6105a5366004615371565b6116d7565b3480156105b657600080fd5b5061052e6105c53660046151dc565b5490565b3480156105d557600080fd5b506105de6117ca565b60405161036191906153fa565b3480156105f757600080fd5b5061052e7f0000000000000000000000000000000000000000000000000000000000009c4081565b61063261062d3660046152ec565b611b54565b6040516103619291906155f2565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e81156001810361068357816000803e816000fd5b50505092915050565b60008060008061069d6001546107ad565b9296509094509250905067ffffffffffffffff8411806106c4575067ffffffffffffffff83115b806106d6575067ffffffffffffffff82115b806106e8575067ffffffffffffffff81115b15610723576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792d60048201526024016102c4565b604083811b8517608084901b1760c083901b17600855600180547ffffffffffffffffffffffffff800000003ffffffffffffffffffffffffffffff1642607a1b179055517fcde545703e0372175cadfff811d67c32910c3dcb33199679b3271c4106afdf9a9061079f9084908490918252602082015260400190565b60405180910390a190919293565b60085460009081908190819067ffffffffffffffff80821690604083901c16836107e2607a8a901c6401ffffffff164261566f565b9050614e207f0000000000000000000000000000000000000000000000000000000000009c4014806108355750619c407f0000000000000000000000000000000000000000000000000000000000009c40145b156108e75764e8d4a51000975067ffffffffffffffff608085901c169550886001166001036108a65761086e6301e13380612710615682565b60018a901c617fff166108818389615682565b61088b9190615682565b61089591906156c8565b61089f9087615703565b9550610a5c565b6108b66301e13380612710615682565b60018a901c617fff166108c98389615682565b6108d39190615682565b6108dd91906156c8565b61089f908761566f565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201526109bf907f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024015b602060405180830381865afa158015610996573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ba9190615716565b611c8f565b50975082881015610a00576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793260048201526024016102c4565b61271061ffff8a16670de0b6b3a7640000858b670de0b6b3a76400000281610a2a57610a2a615699565b04030281610a3a57610a3a615699565b670de0b6b3a7640000919004810167ffffffffffffffff608087901c16020495505b6175307f0000000000000000000000000000000000000000000000000000000000009c401480610aad5750619c407f0000000000000000000000000000000000000000000000000000000000009c40145b15610b595764e8d4a51000965060c084901c9450600160108a901c81169003610b1857610ae06301e13380612710615682565b60118a901c617fff16610af38388615682565b610afd9190615682565b610b0791906156c8565b610b119086615703565b9450610c86565b610b286301e13380612710615682565b60118a901c617fff16610b3b8388615682565b610b459190615682565b610b4f91906156c8565b610b11908661566f565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006004820152610bef907f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401610979565b97505081871015610c30576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793260048201526024016102c4565b61271061ffff60108b901c16670de0b6b3a7640000848a670de0b6b3a76400000281610c5e57610c5e615699565b04030281610c6e57610c6e615699565b670de0b6b3a7640000919004810160c086901c020494505b505050509193509193565b600b54600090819073ffffffffffffffffffffffffffffffffffffffff1661dead14610ced576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055604080516020601f36908101829004820283018201909352828252610d79927f000000000000000000000000b6ff36e7d04e6017e0323f1f92a8184370d489079291600091908190840183828082843760009201919091525061064092505050565b806020019051810190610d8c919061572f565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790559097909650945050505050565b600080808080868180600189901c62ffffff168b9003610e0b575050601a87901c633fffffff169250603887901c6603ffffffffffff166001601989901c811614610e62565b60008c8152600660205260408120600360028e0181810660550292918491048152602081019190915260400160002054901c600181811c633fffffff169750601f82901c6603ffffffffffff169450908116149150505b60008581526007602052604090205493508015610ea5577f80000000000000000000000000000000000000000000000000000000000000009b5060009950611071565b83600316600203610f0f57610ec782607486901c6603ffffffffffff16611e9d565b91506001610ed7600f6023615703565b6001901b610ee5919061566f565b8214610f0f5760a69390931c633fffffff1660008181526007602052604090205490945092610ea5565b8360031660031480610f3b57506001610f2a600f6023615703565b6001901b610f38919061566f565b82145b15610f6c577f80000000000000000000000000000000000000000000000000000000000000009b5060009950611071565b610f848a607486901c6603ffffffffffff1684611f37565b9950606483048a1115610fa15761271061270f8b02049950610fa6565b600099505b891561104d5783600416600414610fc9576207ffff600385901c16600003610fd4565b6207ffff600385901c165b9b506000610fe48d60020b611f7c565b905061271f612710820204633fffffff601687901c8116611005838561566f565b61100f9190615682565b61101991906156c8565b6110239082615703565b61103a6c010000000000000000000000008e615682565b61104491906156c8565b97505050611071565b7f80000000000000000000000000000000000000000000000000000000000000009b505b8b8a97509750505050945094509450945094565b600b5460009081908190819073ffffffffffffffffffffffffffffffffffffffff1661dead146110e5576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055600080549060018216900361112a5760018117600055611160565b6040517f60121cca00000000000000000000000000000000000000000000000000000000815261791960048201526024016102c4565b600061116c344761566f565b905073ffffffffffffffffffffffffffffffffffffffff8816156111905787611192565b335b975060006111a18f8f8f6121de565b90506111b0818d8b8b876123c0565b8060200190518101906111c39190615753565b9198509650925080871015611208576040517f60121cca0000000000000000000000000000000000000000000000000000000081526188ba60048201526024016102c4565b611214868c8c8c613cb9565b6000859055909550935061122782613fe5565b5050600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905550929b919a509850909650945050505050565b600080549060018216900361112a5760018117600055821561128a578261129c565b6fffffffffffffffffffffffffffffffff5b92506112be6fffffffffffffffffffffffffffffffff600061dead85856123c0565b600080fd5b600b546000908190819081908190819073ffffffffffffffffffffffffffffffffffffffff1661dead14611327576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055604080518082019091526000808252602082015260008054808352600116900361112a578051600117600055611387344761566f565b602082015273ffffffffffffffffffffffffffffffffffffffff8916156113ae57886113b0565b335b98506113c38f8d8b8b85600001516123c0565b8060200190518101906113d69190615753565b835290975093508e15611405576113ee878f8f614039565b90965094506113ff848c8c8c613cb9565b90935091505b8051600055602081015161141890613fe5565b50600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055949d939c50919a509850965090945092505050565b600b546000908190819073ffffffffffffffffffffffffffffffffffffffff1661dead146114b8576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055604080516020601f36908101829004820283018201909352828252611544927f00000000000000000000000097fc217b251b50e42d4d2b99e3fb6a7b671484619291600091908190840183828082843760009201919091525061064092505050565b8060200190518101906115579190615753565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055919d909c50909a5098505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4516148061161057503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4516145b61164a576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793c60048201526024016102c4565b60005460011660000361168d576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792a60048201526024016102c4565b600b546116d390839073ffffffffffffffffffffffffffffffffffffffff167f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497846144da565b5050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497161461174a576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792960048201526024016102c4565b60005460011660000361178d576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792a60048201526024016102c4565b6117c48461179d83850185615781565b7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497866144da565b50505050565b611869604080516102008101825260008082526020808301829052828401829052606083018290526080830182905260a0830182905260c0830182905260e0830182905283518085018552828152808201839052610100840152835180850190945281845283015290610120820190815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497811682527f000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d81166020808401919091527f00000000000000000000000097fc217b251b50e42d4d2b99e3fb6a7b67148461821660408401527f000000000000000000000000827b69a0fe822b44286c83d671eda78ee40f138d821660608401527f000000000000000000000000b6ff36e7d04e6017e0323f1f92a8184370d48907821660808401527f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be821660a08401527f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee45821660c08401527f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee45821660e0840152610100830180517f000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583184169052517f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831690820152610120830180517f000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583184169052517f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9092169101527f00000000000000000000000000000000000000000000000000000000000000166101408201527f0000000000000000000000000000000000000000000000000000000000009c406101608201527f00000000000000000000000000000000000000000000000000000000000000006101808201527f00000000000000000000000000000000000000000000000000000000000000006101a08201527f18b0d2b1c86d0f8d3f949ae13f3c0e8a901b02dc6720e6b1170940b98ba25f2a6101c08201527fe18a5692bf658b6b1c214d6f6901b5945c696d883890d12b0528a93d9bccfbb26101e082015290565b600b5460009060609073ffffffffffffffffffffffffffffffffffffffff1661dead14611bb1576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793d60048201526024016102c4565b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055604080516020601f36908101829004820283018201909352828252611c3d927f00000000000000000000000097fc217b251b50e42d4d2b99e3fb6a7b671484619291600091908190840183828082843760009201919091525061064092505050565b806020019051810190611c5091906157cd565b91509150600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905590999098509650505050505050565b67ffffffffffffffff605b82901c811690609b83901c16811580611cb1575080155b15611ced576040517fd50d75120000000000000000000000000000000000000000000000000000000081526201117160048201526024016102c4565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480611d17575082155b80611d225750806001145b15611d2f57505050915091565b64496cebb80084840283020484019350617fff60db87901c16925082600103611d5a57505050915091565b82600116600103611daf5760019290921c91826c7e37be2022c0914b268000000081611d8857611d88615699565b049250612710601e87901c613fff166b033b2e3c9fd0803ce8000000850102049250611ddc565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b80600116600103611e135760011c61271081016b033b2e3c9fd0803ce8000000820281611e0b57611e0b615699565b049050611e49565b60011c61271081016b033b2e3c9fd0803ce8000000820281611e3757611e37615699565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b6000600f83811c9083901c0281681fffffffffffffffff8211611ec1576022611ec4565b60235b91821c919050617fff858116908516018101614000811015611ee557600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00001617fff811115611f2457506603ffffffffffff9250611f31915050565b80600f84901b1793505050505b92915050565b6000617fff838116908316036081811015611f6f57600f83811c821b9085901c860281611f6657611f66615699565b04915050611f75565b60009150505b9392505050565b600060ff82901d80831803617fff811115611f9657600080fd5b7001000000000000000000000000000000006001821615611fc457506fff9dd7de423466c20352b1246ce4856f5b6002821615611fe3576fff3bd55f4488ad277531fa1c725a66d00260801c5b6004821615612002576ffe78410fd6498b73cb96a6917f8532590260801c5b6008821615612021576ffcf2d9987c9be178ad5bfeffaa1232730260801c5b6010821615612040576ff9ef02c4529258b057769680fc6601b30260801c5b602082161561205f576ff402d288133a85a17784a411f7aba0820260801c5b604082161561207e576fe895615b5beb6386553757b0352bda900260801c5b608082161561209d576fd34f17a00ffa00a8309940a15930391a0260801c5b6101008216156120bd576fae6b7961714e20548d88ea5123f9a0ff0260801c5b6102008216156120dd576f76d6461f27082d74e0feed3b388c0ca10260801c5b6104008216156120fd576f372a3bfe0745d8b6b19d985d9a8b85bb0260801c5b61080082161561211d576f0be32cbee48979763cf7247dd7bb539d0260801c5b61100082161561213c576e8d4f70c9ff4924dac37612d1e2921e0260801c5b61200082161561215a576d4e009ae5519380809a02ca7aec770260801c5b614000821615612176576b17c45e641b6e95dee056ff100260801c5b600091507f800000000000000000000000000000000000000000000000000000000000000084166121d4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0463ffffffff8116156121d457600191505b60201c0192915050565b6000831580156121ec575082155b15612227576040517f60121cca0000000000000000000000000000000000000000000000000000000081526188b960048201526024016102c4565b7f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4573ffffffffffffffffffffffffffffffffffffffff16636876698173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583173ffffffffffffffffffffffffffffffffffffffff1614612328577f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461232257600061232a565b8461232a565b855b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810188905260248101879052604481018690526000606482015260840160206040518083038185885af1158015612393573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906123b89190615716565b949350505050565b60606124026040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001548152600283901c620fffff1660000361244e576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791f60048201526024016102c4565b8051612459906107ad565b909192509091508260600183608001828152508281525050506124d66040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b61250f6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60008061254a7f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be633fffffff605c8860000151901c166145a1565b73ffffffffffffffffffffffffffffffffffffffff1663f3190c896040518163ffffffff1660e01b8152600401602060405180830381865afa158015612594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b89190615716565b9150760a70c3c40a64e6c51999090b65f67d92400000000000008211806125dd575081155b15612618576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793960048201526024016102c4565b8460800151856060015183028161263157612631615699565b049150722cd76fe086b93ce2f768a00b22a0000000000082111561266657722cd76fe086b93ce2f768a00b22a0000000000091505b81760a70c3c40a64e6c51999090b65f67d92400000000000008161268c5761268c615699565b865191900491506127109060481c6103ff16810182020460608501526b033b2e3c9fd0803ce80000006c010000000000000000000000008302865191900492506103e890602a1c6103ff1683020490506126e581614a0b565b50602086015284516103e89060341c6103ff16830204905061270681614a0b565b506040860152600480881614612728576207ffff600388901c16600003612733565b6207ffff600388901c165b60c0850181905260408601511215612888576128047f000000000000000000000000b6ff36e7d04e6017e0323f1f92a8184370d48907888760400151604051602401612789929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9e3e482100000000000000000000000000000000000000000000000000000000179052610640565b8060200190518101906128179190615716565b965086600416600414612836576207ffff600388901c16600003612841565b6207ffff600388901c165b60c085015260008b90036128885760408051600060208201819052918101919091526060810188905260800160405160208183030381529060405295505050505050613cb0565b6127108b10806128a757506fffffffffffffffffffffffffffffffff8b115b156128e2576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792e60048201526024016102c4565b60028716156128f25760026128f5565b60015b60ff16846101000181815250506129426040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60c08501518152601688901c633fffffff16808552600090815260076020908152604082205490860181905260741c6603ffffffffffff16606086018190529003612995576603ffffffffc00060608501525b602084015160c41c620fffff16925082156129dc57826001166001146129c7576207ffff600184901c166000036129d2565b6207ffff600184901c165b6080850152612a03565b7f800000000000000000000000000000000000000000000000000000000000000060808501525b856080015164e8d4a510008d0281612a1d57612a1d615699565b0480865266ffffffffffffff609a8a901c1660ff60928b901c161b9250612a48633b9aca00846156c8565b1115612a84576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792e60048201526024016102c4565b8815612b175760025485516fffffffffffffffffffffffffffffffff8216945060809190911c9250831115612afc5784518390612ac19084615682565b612acb91906156c8565b60a08601819052612adc908361566f565b855160808088018290526000885282901b94039384176002559150612b17565b6000600255845183900385526080850183905260a085018290525b85602001518560c0015113156136c4578451156136c4576040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081018290528190680100000000000000009060008960c0015112612b93576101008960c0015181612b8d57612b8d615699565b05612bb0565b60016101008a60c0015160010181612bad57612bad615699565b05035b60408201528451612bc090611f7c565b6040860152610100890151600103612beb57604085015160e08a015260c08901516020820152612d10565b61271f85604001516127100281612c0457612c04615699565b046060860181905260408601510360808601819052602089015160161c633fffffff90811660c0880181905290910260608701519190040160e08a0152845160208b0151600101148015612c5c57508460c001516001145b15612d10577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff215373ffffffffffffffffffffffffffffffffffffffff8f1601612cda576040517fcc36aa4400000000000000000000000000000000000000000000000000000000815260006004820181905260248201526044016102c4565b6040517f60121cca00000000000000000000000000000000000000000000000000000000815261793560048201526024016102c4565b886101000151600103612d805760c0890180516000908152600560205260408082205460608c01518c5194518452919092206301fffffe8316601a9490941b9390931760389190911b1760011790915566ffffffffffffff602182901c1660ff601983901c161b97509550612da3565b67ffffffffffffffff60348960200151901c16965060ff8716600888901c901b96505b8689602001818151612db59190615703565b90525060e0890151612dd46c0100000000000000000000000089615682565b612dde91906156c8565b89604001818151612def9190615703565b90525060c08901516020820151148015612e0e57508861010001516001145b80612e1b57506080810151155b15612f4b578060800151600003612e445760408082015160009081526004602052205460808201525b886101000151600103612e9b57604081015160c08a015160009081036101009283020190910160608301819052608083018051821b90911c908190529003612e9b5760408082015160009081526004602052908120555b608081015115612ec9576001612eb48260800151614e20565b60408301516101000201036020820152612f4b565b89602001518160400151610100021215612f08577f80000000000000000000000000000000000000000000000000000000000000006020820152612f4b565b604080820180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01908190526000908152600460205220546080820152612e9b565b80602001518860800151138015612f69575089602001518860800151135b15612f865760808801516101208a015260026101608a0152612fc2565b896020015181602001511315612fae5760208101516101208a015260016101608a0152612fc2565b60208a01516101208a015260036101608a01525b612fd389610120015160020b611f7c565b60408601526101608901516002036130675761271f85604001516127100281612ffe57612ffe615699565b046060860181905260408087015191909103608087019081526020808b015160a61c633fffffff908116600090815260079092529290205460a08b0181905260161c821660c0880181905290510260608701519190040160a086018190526101408a0152613084565b604085015160a086018190526101408a0152633fffffff60c08601525b6c010000000000000000000000008961014001518a606001516130a79190615682565b6130b191906156c8565b6130c7906b033b2e3c9fd0803ce800000061566f565b8960e001518a602001518b61014001516130e19190615682565b6130eb91906156c8565b8a602001516130fa919061566f565b613110906b033b2e3c9fd0803ce8000000615682565b61311a91906156c8565b93506b033b2e3c9fd0803ce80000008960600151856131399190615682565b61314391906156c8565b92508389602001510361315e5761315b60018561566f565b93505b88518410158061317357508861016001516003145b156134de5760808101516040808301516000908152600460205220558851841061335357885160608a01519094506b033b2e3c9fd0803ce8000000906131b99086615682565b6131c391906156c8565b92508289604001516131d5919061566f565b6c01000000000000000000000000858b602001516131f3919061566f565b6131fd9190615682565b61320791906156c8565b965061321287614a0b565b60608701528086526101208a01511380156132345750633fffffff8560c00151145b1561324c5784516002018552600160c0860152613366565b84516001018552610160890151600214801561326d57506101208901518551145b61327857600061327e565b8460c001515b9550612710856060015161271f028161329957613299615699565b046040860181905260608601519081900360808701819052908803633fffffff02816132c7576132c7615699565b0460c08601819052156132f857633fffffff8560c0015110156132ee578460c001516132fb565b633ffffffe6132fb565b60015b60c0860152851580159061331357508460c001518610155b1561334e576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793860048201526024016102c4565b613366565b6101208901516001908101865260c08601525b6020890151613375858261566f565b61337f9084615682565b61338991906156c8565b9150838960800181815161339d9190615703565b9052506020890180518591906133b490839061566f565b90525060a0890180518491906133cb908390615703565b9052506040890180518491906133e290839061566f565b90525060608801516133f49083614ed6565b606089015260208901516064111561343c576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793060048201526024016102c4565b8451600013613452578451600190811b1761345b565b845160000360011b5b606089015160208b015191975060741b9060349061347e90603860086001614f89565b901b60168760c00151901b600289901b60a6808d60200151901c901b60011717171717600760008a6000015181526020019081526020016000208190555060168860000151901b600287901b6034808f901c901b60021717179b506136bf565b8851849003895260208901516134f4858261566f565b6134fe9084615682565b61350891906156c8565b9150838960800181815161351c9190615703565b90525060208901805185919061353390839061566f565b90525060a08901805184919061354a908390615703565b90525060408901805184919061356190839061566f565b90525060608801516135739083614ed6565b606089015261016089015168010000000000000000925060020361369857633fffffff60a68960200151901c1696508760a00151955060006603ffffffffffff607488901c16905060006135d48a60600151836150b290919063ffffffff16565b6020808c0180518d516000908152600790935260409092207fffffffffffffffffffffffc00000000000000000000000000000000000000000909216607484901b17600217909155998b5298889052506060890181905260c487901c620fffff1697881561366e5788600116600114613659576207ffff60018a901c16600003613664565b6207ffff60018a901c165b60808b0152613695565b7f800000000000000000000000000000000000000000000000000000000000000060808b01525b50505b61012089015160c08a01526101608901516101008a015261014089015160e08a0152612d10565b505050505b64e8d4a51000866080015186608001516136de9190615682565b6136e891906156c8565b8660a001818152505064e8d4a5100086606001518660a0015161370b9190615682565b61371591906156c8565b60c087015260a08601518c10156137515760a0860151613735908d6156c8565b8660c001516137449190615682565b60c087015260a086018c90525b8560a00151600003613793576040517f60121cca00000000000000000000000000000000000000000000000000000000815261793560048201526024016102c4565b8a8660a001518760c00151670de0b6b3a76400006137b19190615682565b6137bb91906156c8565b10156137f7576040517f60121cca00000000000000000000000000000000000000000000000000000000815261792160048201526024016102c4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff215373ffffffffffffffffffffffffffffffffffffffff8b16016138795760c086015160a08701516040517fcc36aa44000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016102c4565b6175307f0000000000000000000000000000000000000000000000000000000000009c4014806138ca5750619c407f0000000000000000000000000000000000000000000000000000000000009c40145b613a2f577fffffffffffffffffffffffff11111111111111111111111111111111111111127f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4573ffffffffffffffffffffffffffffffffffffffff1601613937578560a00151925061393c565b600092505b7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff1663ad967e15847f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4560008a60a001516139aa906158b5565b604080513360208201526000918291016040516020818303038152906040526040518863ffffffff1660e01b81526004016139ea96959493929190615951565b604080518083038185885af1158015613a07573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613a2c919061572f565b50505b614e207f0000000000000000000000000000000000000000000000000000000000009c401480613a805750619c407f0000000000000000000000000000000000000000000000000000000000009c40145b613b69577f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff1663ad967e157f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee458860c00151613aef906158b5565b604080516000808252602082019092528f9082906040518763ffffffff1660e01b8152600401613b2496959493929190615951565b60408051808303816000875af1158015613b42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b66919061572f565b50505b67ffffffffffffffff605289901c1692508460a0015160ff8416600885901c901b613b94919061566f565b925067ffffffffffffffff609289901c169150846080015160ff8316600884901c901b613bc1919061566f565b91506092613bd483603860086001614f89565b901b6052613be785603860086000614f89565b60c089015160a08a015160408051338152602081019390935282015273ffffffffffffffffffffffffffffffffffffffff8e1660608201527ffffffffffffc00000000000000000000000000000003ffffffffffffffffffff909b16911b1717977f80fd9cc6b1821f4a510e45ffce6852ea3404807b5d3d833ffa85664408afcb669060800160405180910390a160a086015160c08701516040805160208101939093528201526060810189905260800160405160208183030381529060405296505050505050505b95945050505050565b60008080670de0b6b3a7640000613cd08888615682565b613cda91906156c8565b90506000670de0b6b3a7640000613cf18988615682565b613cfb91906156c8565b9050600082118015613d0d5750600081115b15613de2576040517f35f0df9800000000000000000000000000000000000000000000000000000000815260048101899052602481018390526044810182905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4516906335f0df989060840160408051808303816000875af1158015613db4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd8919061572f565b9094509250613fda565b600082118015613df0575080155b15613ec3576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101899052602481018390526044810182905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee451690634c89bfd4906084016020604051808303816000875af1158015613e98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ebc9190615716565b9350613fda565b81158015613ed15750600081115b15613fa4576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101899052602481018390526044810182905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee451690634c89bfd4906084016020604051808303816000875af1158015613f79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f9d9190615716565b9250613fda565b6040517f60121cca0000000000000000000000000000000000000000000000000000000081526188b960048201526024016102c4565b505094509492505050565b303181811115613ffb576116d33383830361515d565b818110156116d3576040517f60121cca00000000000000000000000000000000000000000000000000000000815261791b60048201526024016102c4565b60008080670de0b6b3a76400006140508787615682565b61405a91906156c8565b90506000670de0b6b3a76400006140718887615682565b61407b91906156c8565b905060008211801561408d5750600081115b1561422c577f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4573ffffffffffffffffffffffffffffffffffffffff16635b3d38d773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583173ffffffffffffffffffffffffffffffffffffffff1614614193577f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461418d576000614195565b82614195565b835b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b168152600481018b9052602481018690526044810185905260006064820152608401604080518083038185885af11580156141fd573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190614222919061572f565b90945092506144d0565b60008211801561423a575080155b15614380577f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4573ffffffffffffffffffffffffffffffffffffffff166330acd6fd73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583173ffffffffffffffffffffffffffffffffffffffff16146142e95760006142eb565b835b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b168152600481018b905260248101869052604481018590526000606482015260840160206040518083038185885af1158015614354573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906143799190615716565b93506144d0565b8115801561438e5750600081115b15613fa4577f000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee4573ffffffffffffffffffffffffffffffffffffffff166330acd6fd73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff161461443d57600061443f565b825b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b168152600481018b905260248101869052604481018590526000606482015260840160206040518083038185885af11580156144a8573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906144cd9190615716565b92505b5050935093915050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061459a576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155960048201526024016102c4565b5050505050565b60006060826000036145b7576000915050611f31565b607f8311614680576040517fd60000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16602282015260f884901b7fff000000000000000000000000000000000000000000000000000000000000001660368201526037015b60405160208183030381529060405290506149fc565b60ff831161475d576040517fd70000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f8100000000000000000000000000000000000000000000000000000000000000603682015260f884901b7fff0000000000000000000000000000000000000000000000000000000000000016603782015260380161466a565b61ffff831161483b576040517fd80000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f820000000000000000000000000000000000000000000000000000000000000060368201527fffff00000000000000000000000000000000000000000000000000000000000060f085901b16603782015260390161466a565b62ffffff831161491a576040517fd90000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f830000000000000000000000000000000000000000000000000000000000000060368201527fffffff000000000000000000000000000000000000000000000000000000000060e885901b166037820152603a0161466a565b6040517fda0000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f840000000000000000000000000000000000000000000000000000000000000060368201527fffffffff0000000000000000000000000000000000000000000000000000000060e085901b166037820152603b0160405160208183030381529060405290505b80516020909101209392505050565b600080630235b88083107473d85bca016a2338b31715f8e13054c005f8b995d384111715614a3857600080fd5b6c010000000000000000000000008310600081614a6e57506c010000000000000000000000006a52b7d2dcc80cd2e40000008502045b8115614a9157507652b7d2dcc80cd2e40000000000000000000000000000008490045b6f037af932b2affa9738cc6c38ca5278318110614ad257614000841793506f037af932b2affa9738cc6c38ca5278316a52b7d2dcc80cd2e400000082020490505b6d010f7a088a76f267264caa114f0a8110614b0f57612000841793506d010f7a088a76f267264caa114f0a6a52b7d2dcc80cd2e400000082020490505b6b95da74f87f839fc2e0dc5bd98110614b4857611000841793506b95da74f87f839fc2e0dc5bd96a52b7d2dcc80cd2e400000082020490505b6b06f55dedafd8491caed5a1b88110614b8157610800841793506b06f55dedafd8491caed5a1b86a52b7d2dcc80cd2e400000082020490505b6b017fdd10ee11e624491b4cc18110614bba57610400841793506b017fdd10ee11e624491b4cc16a52b7d2dcc80cd2e400000082020490505b6ab23131bf0c30217b0a2c698110614bf157610200841793506ab23131bf0c30217b0a2c696a52b7d2dcc80cd2e400000082020490505b6a79683edcb9280d797aded78110614c2857610100841793506a79683edcb9280d797aded76a52b7d2dcc80cd2e400000082020490505b6a64366e2f9919f0d9b0dc908110614c5e576080841793506a64366e2f9919f0d9b0dc906a52b7d2dcc80cd2e400000082020490505b6a5b0bcda5a78850646b0a818110614c94576040841793506a5b0bcda5a78850646b0a816a52b7d2dcc80cd2e400000082020490505b6a56c840f992c70f959ae8108110614cca576020841793506a56c840f992c70f959ae8106a52b7d2dcc80cd2e400000082020490505b6a54b9cd178695194f9be0a08110614d00576010841793506a54b9cd178695194f9be0a06a52b7d2dcc80cd2e400000082020490505b6a53b7458aff204b5e65d6818110614d36576008841793506a53b7458aff204b5e65d6816a52b7d2dcc80cd2e400000082020490505b6a53372a2f38c240d689e4008110614d6c576004841793506a53372a2f38c240d689e4006a52b7d2dcc80cd2e400000082020490505b6a52f76617a04499e66400008110614da2576002841793506a52f76617a04499e66400006a52b7d2dcc80cd2e400000082020490505b6a52d79660f3dec355c000008110614dd8576001841793506a52d79660f3dec355c000006a52b7d2dcc80cd2e400000082020490505b81614df057806a52b7d2dcc80cd2e400000086020492505b8115614e0c579219926a52d79660f3dec355c000008582020492505b505082811115614e1b57600080fd5b915091565b6000816fffffffffffffffffffffffffffffffff831115614e425760809150811c5b67ffffffffffffffff811115614e5a576040918201911c5b63ffffffff811115614e6e576020918201911c5b61ffff811115614e80576010918201911c5b60ff811115614e91576008918201911c5b600f811115614ea2576004918201911c5b6003811115614eb3576002918201911c5b6001811115614ec3576001820191505b8015614ed0576001820191505b50919050565b6000600f83901c8202816c03ffffffffffffffffffffffff8211614f1d576c01ffffffffffffffffffffffff8211614f1657614f1182614e20565b614f20565b6062614f20565b60635b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd617fff87168201810194500191821c91905060408311156112be5750600f1b017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00192915050565b600080856fffffffffffffffffffffffffffffffff811115614fac5760809150811c5b67ffffffffffffffff811115614fc4576040918201911c5b63ffffffff811115614fd8576020918201911c5b61ffff811115614fea576010918201911c5b60ff811115614ffb576008918201911c5b600f81111561500c576004918201911c5b600381111561501d576002918201911c5b600181111561502d576001820191505b801561503a576001820191505b50848110156150465750835b848103905085811c6000821184161561509557600181019050806001871b0361509557506001908101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86011b5b6001851b82106150a457600080fd5b90931b909201949350505050565b600080600f83901c7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000603186901b16816150ee576150ee615699565b0490506000604082901c600114615106576040615109565b60415b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd81019290921c91617fff8581166040019250613fdd90871690910101818111156112be5703600f9190911b179050611f31565b60008060008060008587614e20f19050806151a9576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016102c4565b505050565b80151581146151bc57600080fd5b50565b6000602082840312156151d157600080fd5b8151611f75816151ae565b6000602082840312156151ee57600080fd5b5035919050565b6000806000806080858703121561520b57600080fd5b5050823594602084013594506040840135936060013592509050565b73ffffffffffffffffffffffffffffffffffffffff811681146151bc57600080fd5b600080600080600080600080610100898b03121561526657600080fd5b883597506020890135965060408901359550606089013594506080890135935060a0890135925060c089013561529b81615227565b915060e08901356152ab816151ae565b809150509295985092959890939650565b600080604083850312156152cf57600080fd5b8235915060208301356152e1816151ae565b809150509250929050565b600080600080600080600080610100898b03121561530957600080fd5b883597506020890135965060408901359550606089013594506080890135935060a0890135925060c0890135915060e08901356152ab81615227565b6000806040838503121561535857600080fd5b823561536381615227565b946020939093013593505050565b6000806000806060858703121561538757600080fd5b843561539281615227565b935060208501359250604085013567ffffffffffffffff808211156153b657600080fd5b818701915087601f8301126153ca57600080fd5b8135818111156153d957600080fd5b8860208285010111156153eb57600080fd5b95989497505060200194505050565b815173ffffffffffffffffffffffffffffffffffffffff16815261024081016020830151615440602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151615468604084018273ffffffffffffffffffffffffffffffffffffffff169052565b506060830151615490606084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060808301516154b8608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a08301516154e060a084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c083015161550860c084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060e083015161553060e084018273ffffffffffffffffffffffffffffffffffffffff169052565b5061010083810151805173ffffffffffffffffffffffffffffffffffffffff90811685840152602082015116610120850152505061012083015161014061559c81850183805173ffffffffffffffffffffffffffffffffffffffff908116835260209182015116910152565b840151610180848101919091526101608501516101a080860191909152908501516101c080860191909152908501516101e080860191909152908501516102008501529093015161022090920191909152919050565b6000604082018483526020604081850152818551808452606086019150828701935060005b8181101561563357845183529383019391830191600101615617565b5090979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115611f3157611f31615640565b8082028115828204841417611f3157611f31615640565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826156fe577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b80820180821115611f3157611f31615640565b60006020828403121561572857600080fd5b5051919050565b6000806040838503121561574257600080fd5b505080516020909101519092909150565b60008060006060848603121561576857600080fd5b8351925060208401519150604084015190509250925092565b60006020828403121561579357600080fd5b8135611f7581615227565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080604083850312156157e057600080fd5b8251915060208084015167ffffffffffffffff8082111561580057600080fd5b818601915086601f83011261581457600080fd5b8151818111156158265761582661579e565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f830116810181811085821117156158695761586961579e565b60405291825284820192508381018501918983111561588757600080fd5b938501935b828510156158a55784518452938501939285019261588c565b8096505050505050509250929050565b60007f800000000000000000000000000000000000000000000000000000000000000082036158e6576158e6615640565b5060000390565b6000815180845260005b81811015615913576020818501810151868301820152016158f7565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352876020840152866040840152808616606084015280851660808401525060c060a083015261599e60c08301846158ed565b9897505050505050505056fea26469706673582212202cd8364122fdd123f7e831cb09cc663a8e3a459240ed4213bdb860def13d8fb264736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d00000000000000000000000097fc217b251b50e42d4d2b99e3fb6a7b67148461000000000000000000000000827b69a0fe822b44286c83d671eda78ee40f138d000000000000000000000000b6ff36e7d04e6017e0323f1f92a8184370d489070000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee45000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee45000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000009c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018b0d2b1c86d0f8d3f949ae13f3c0e8a901b02dc6720e6b1170940b98ba25f2ae18a5692bf658b6b1c214d6f6901b5945c696d883890d12b0528a93d9bccfbb2
-----Decoded View---------------
Arg [0] : constants_ (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497
Arg [1] : 000000000000000000000000324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d
Arg [2] : 00000000000000000000000097fc217b251b50e42d4d2b99e3fb6a7b67148461
Arg [3] : 000000000000000000000000827b69a0fe822b44286c83d671eda78ee40f138d
Arg [4] : 000000000000000000000000b6ff36e7d04e6017e0323f1f92a8184370d48907
Arg [5] : 0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be
Arg [6] : 000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee45
Arg [7] : 000000000000000000000000de632c3a214d5f14c1d8ddf0b92f8bcd188fee45
Arg [8] : 000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831
Arg [9] : 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
Arg [10] : 000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831
Arg [11] : 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000016
Arg [13] : 0000000000000000000000000000000000000000000000000000000000009c40
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [16] : 18b0d2b1c86d0f8d3f949ae13f3c0e8a901b02dc6720e6b1170940b98ba25f2a
Arg [17] : e18a5692bf658b6b1c214d6f6901b5945c696d883890d12b0528a93d9bccfbb2
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.