Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
21441345 | 5 mins ago | 1.97509757 ETH | ||||
21441345 | 5 mins ago | 1.97509757 ETH | ||||
21441183 | 37 mins ago | 4.78262108 ETH | ||||
21441183 | 37 mins ago | 4.78262108 ETH | ||||
21440583 | 2 hrs ago | 1.50143068 ETH | ||||
21440583 | 2 hrs ago | 1.50143068 ETH | ||||
21440567 | 2 hrs ago | 2 ETH | ||||
21440567 | 2 hrs ago | 2 ETH | ||||
21439468 | 6 hrs ago | 8.62507524 ETH | ||||
21439468 | 6 hrs ago | 8.62507524 ETH | ||||
21439415 | 6 hrs ago | 2.16874999 ETH | ||||
21439415 | 6 hrs ago | 2.16874999 ETH | ||||
21439229 | 7 hrs ago | 20.37876287 ETH | ||||
21439229 | 7 hrs ago | 20.37876287 ETH | ||||
21439209 | 7 hrs ago | 14.41300502 ETH | ||||
21439209 | 7 hrs ago | 14.41300502 ETH | ||||
21438938 | 8 hrs ago | 14.70395438 ETH | ||||
21438938 | 8 hrs ago | 14.70395438 ETH | ||||
21438324 | 10 hrs ago | 7.27263671 ETH | ||||
21438324 | 10 hrs ago | 7.27263671 ETH | ||||
21438309 | 10 hrs ago | 7.24444625 ETH | ||||
21438309 | 10 hrs ago | 7.24444625 ETH | ||||
21438207 | 10 hrs ago | 2.96950897 ETH | ||||
21438207 | 10 hrs ago | 2.96950897 ETH | ||||
21438112 | 10 hrs ago | 3.00182613 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:
FluidDexT1
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 { CoreHelpers } from "../helpers/coreHelpers.sol"; import { SafeTransfer } from "../../../../../libraries/safeTransfer.sol"; import { DexSlotsLink } from "../../../../../libraries/dexSlotsLink.sol"; import { DexCalcs } from "../../../../../libraries/dexCalcs.sol"; import { BigMathMinified } from "../../../../../libraries/bigMathMinified.sol"; import { ErrorTypes } from "../../../errorTypes.sol"; import { IFluidDexT1 } from "../../../interfaces/iDexT1.sol"; interface IDexCallback { function dexCallback(address token_, uint256 amount_) external; } /// @title FluidDexT1 /// @notice Implements core logics for Fluid Dex protocol. /// Note Token transfers happen directly from user to Liquidity contract and vice-versa. contract FluidDexT1 is CoreHelpers { using BigMathMinified for uint256; constructor(ConstantViews memory constantViews_) CoreHelpers(constantViews_) { // any implementations should not be zero if ( constantViews_.implementations.shift == address(0) || constantViews_.implementations.admin == address(0) || constantViews_.implementations.colOperations == address(0) || constantViews_.implementations.debtOperations == address(0) || constantViews_.implementations.perfectOperationsAndSwapOut == address(0) ) { revert FluidDexError(ErrorTypes.DexT1__InvalidImplementation); } } struct SwapInExtras { address to; uint amountOutMin; bool isCallback; } /// @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 extras_ Additional parameters for the swap: /// - 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_ /// - amountOutMin: The minimum amount of output tokens the user expects to receive /// - isCallback: If true, indicates that the input tokens should be transferred via a callback /// @return amountOut_ The amount of output tokens received from the swap function _swapIn( bool swap0to1_, uint256 amountIn_, SwapInExtras memory extras_ ) internal returns (uint256 amountOut_) { uint dexVariables_ = dexVariables; uint dexVariables2_ = dexVariables2; if ((dexVariables2_ >> 255) == 1) revert FluidDexError(ErrorTypes.DexT1__SwapAndArbitragePaused); _check(dexVariables_, dexVariables2_); if (extras_.to == address(0)) extras_.to = msg.sender; SwapInMemory memory s_; if (swap0to1_) { (s_.tokenIn, s_.tokenOut) = (TOKEN_0, TOKEN_1); unchecked { s_.amtInAdjusted = (amountIn_ * TOKEN_0_NUMERATOR_PRECISION) / TOKEN_0_DENOMINATOR_PRECISION; } } else { (s_.tokenIn, s_.tokenOut) = (TOKEN_1, TOKEN_0); unchecked { s_.amtInAdjusted = (amountIn_ * TOKEN_1_NUMERATOR_PRECISION) / TOKEN_1_DENOMINATOR_PRECISION; } } _verifySwapAndNonPerfectActions(s_.amtInAdjusted, amountIn_); PricesAndExchangePrice memory pex_ = _getPricesAndExchangePrices(dexVariables_, dexVariables2_); if (msg.value > 0) { if (msg.value != amountIn_) revert FluidDexError(ErrorTypes.DexT1__EthAndAmountInMisMatch); if (s_.tokenIn != NATIVE_TOKEN) revert FluidDexError(ErrorTypes.DexT1__EthSentForNonNativeSwap); } // is smart collateral pool enabled uint temp_ = dexVariables2_ & 1; // is smart debt pool enabled uint temp2_ = (dexVariables2_ >> 1) & 1; uint temp3_; uint temp4_; // extracting fee temp3_ = ((dexVariables2_ >> 2) & X17); unchecked { // revenueCut in 6 decimals, to have proper precision // if fee = 1% and revenue cut = 10% then revenueCut = 1e8 - (10000 * 10) = 99900000 s_.revenueCut = EIGHT_DECIMALS - ((((dexVariables2_ >> 19) & X7) * temp3_)); // fee in 4 decimals // 1 - fee. If fee is 1% then withoutFee will be 1e6 - 1e4 // s_.fee => 1 - withdraw fee s_.fee = SIX_DECIMALS - temp3_; } CollateralReservesSwap memory cs_; DebtReservesSwap memory ds_; if (temp_ == 1) { // smart collateral is enabled { CollateralReserves memory c_ = _getCollateralReserves( pex_.geometricMean, pex_.upperRange, pex_.lowerRange, pex_.supplyToken0ExchangePrice, pex_.supplyToken1ExchangePrice ); if (swap0to1_) { ( cs_.tokenInRealReserves, cs_.tokenOutRealReserves, cs_.tokenInImaginaryReserves, cs_.tokenOutImaginaryReserves ) = ( c_.token0RealReserves, c_.token1RealReserves, c_.token0ImaginaryReserves, c_.token1ImaginaryReserves ); } else { ( cs_.tokenInRealReserves, cs_.tokenOutRealReserves, cs_.tokenInImaginaryReserves, cs_.tokenOutImaginaryReserves ) = ( c_.token1RealReserves, c_.token0RealReserves, c_.token1ImaginaryReserves, c_.token0ImaginaryReserves ); } } } if (temp2_ == 1) { // smart debt is enabled { DebtReserves memory d_ = _getDebtReserves( pex_.geometricMean, pex_.upperRange, pex_.lowerRange, pex_.borrowToken0ExchangePrice, pex_.borrowToken1ExchangePrice ); if (swap0to1_) { ( ds_.tokenInDebt, ds_.tokenOutDebt, ds_.tokenInRealReserves, ds_.tokenOutRealReserves, ds_.tokenInImaginaryReserves, ds_.tokenOutImaginaryReserves ) = ( d_.token0Debt, d_.token1Debt, d_.token0RealReserves, d_.token1RealReserves, d_.token0ImaginaryReserves, d_.token1ImaginaryReserves ); } else { ( ds_.tokenInDebt, ds_.tokenOutDebt, ds_.tokenInRealReserves, ds_.tokenOutRealReserves, ds_.tokenInImaginaryReserves, ds_.tokenOutImaginaryReserves ) = ( d_.token1Debt, d_.token0Debt, d_.token1RealReserves, d_.token0RealReserves, d_.token1ImaginaryReserves, d_.token0ImaginaryReserves ); } } } // limiting amtInAdjusted to be not more than 50% of both (collateral & debt) imaginary tokenIn reserves combined // basically, if this throws that means user is trying to swap 0.5x tokenIn if current tokenIn imaginary reserves is x // let's take x as token0 here, that means, initially the pool pricing might be: // token1Reserve / x and new pool pricing will become token1Reserve / 1.5x (token1Reserve will decrease after swap but for simplicity ignoring that) // So pool price is decreased by ~33.33% (oracle will throw error in this case as it only allows 5% price difference but better to limit it before hand) unchecked { if (s_.amtInAdjusted > ((cs_.tokenInImaginaryReserves + ds_.tokenInImaginaryReserves) / 2)) revert FluidDexError(ErrorTypes.DexT1__SwapInLimitingAmounts); } if (temp_ == 1 && temp2_ == 1) { // unless both pools are enabled s_.swapRoutingAmt will be 0 s_.swapRoutingAmt = _swapRoutingIn( s_.amtInAdjusted, cs_.tokenOutImaginaryReserves, cs_.tokenInImaginaryReserves, ds_.tokenOutImaginaryReserves, ds_.tokenInImaginaryReserves ); } // In below if else statement temps are: // temp_ => deposit amt // temp2_ => withdraw amt // temp3_ => payback amt // temp4_ => borrow amt if (int(s_.amtInAdjusted) > s_.swapRoutingAmt && s_.swapRoutingAmt > 0) { // swap will route from the both pools // temp_ = amountInCol_ temp_ = uint(s_.swapRoutingAmt); unchecked { // temp3_ = amountInDebt_ temp3_ = s_.amtInAdjusted - temp_; } (temp2_, temp4_) = (0, 0); // debt pool price will be the same as collateral pool after the swap s_.withdrawTo = extras_.to; s_.borrowTo = extras_.to; } else if ((temp_ == 1 && temp2_ == 0) || (s_.swapRoutingAmt >= int(s_.amtInAdjusted))) { // entire swap will route through collateral pool (temp_, temp2_, temp3_, temp4_) = (s_.amtInAdjusted, 0, 0, 0); // price can slightly differ from debt pool but difference will be very small. Probably <0.01% for active DEX pools. s_.withdrawTo = extras_.to; } else if ((temp_ == 0 && temp2_ == 1) || (s_.swapRoutingAmt <= 0)) { // entire swap will route through debt pool (temp_, temp2_, temp3_, temp4_) = (0, 0, s_.amtInAdjusted, 0); // price can slightly differ from collateral pool but difference will be very small. Probably <0.01% for active DEX pools. s_.borrowTo = extras_.to; } else { // swap should never reach this point but if it does then reverting revert FluidDexError(ErrorTypes.DexT1__NoSwapRoute); } if (temp_ > 0) { // temp2_ = amountOutCol_ temp2_ = _getAmountOut( ((temp_ * s_.fee) / SIX_DECIMALS), cs_.tokenInImaginaryReserves, cs_.tokenOutImaginaryReserves ); swap0to1_ ? _verifyToken1Reserves( (cs_.tokenInRealReserves + temp_), (cs_.tokenOutRealReserves - temp2_), pex_.centerPrice, MINIMUM_LIQUIDITY_SWAP ) : _verifyToken0Reserves( (cs_.tokenOutRealReserves - temp2_), (cs_.tokenInRealReserves + temp_), pex_.centerPrice, MINIMUM_LIQUIDITY_SWAP ); } if (temp3_ > 0) { // temp4_ = amountOutDebt_ temp4_ = _getAmountOut( ((temp3_ * s_.fee) / SIX_DECIMALS), ds_.tokenInImaginaryReserves, ds_.tokenOutImaginaryReserves ); swap0to1_ ? _verifyToken1Reserves( (ds_.tokenInRealReserves + temp3_), (ds_.tokenOutRealReserves - temp4_), pex_.centerPrice, MINIMUM_LIQUIDITY_SWAP ) : _verifyToken0Reserves( (ds_.tokenOutRealReserves - temp4_), (ds_.tokenInRealReserves + temp3_), pex_.centerPrice, MINIMUM_LIQUIDITY_SWAP ); } // (temp_ + temp3_) == amountIn_ == msg.value (for native token), if there is revenue cut then this statement is not true temp_ = (temp_ * s_.revenueCut) / EIGHT_DECIMALS; temp3_ = (temp3_ * s_.revenueCut) / EIGHT_DECIMALS; // from whatever pool higher amount of swap is routing we are taking that as final price, does not matter much because both pools final price should be same if (temp_ > temp3_) { // new pool price from col pool s_.price = swap0to1_ ? ((cs_.tokenOutImaginaryReserves - temp2_) * 1e27) / (cs_.tokenInImaginaryReserves + temp_) : ((cs_.tokenInImaginaryReserves + temp_) * 1e27) / (cs_.tokenOutImaginaryReserves - temp2_); } else { // new pool price from debt pool s_.price = swap0to1_ ? ((ds_.tokenOutImaginaryReserves - temp4_) * 1e27) / (ds_.tokenInImaginaryReserves + temp3_) : ((ds_.tokenInImaginaryReserves + temp3_) * 1e27) / (ds_.tokenOutImaginaryReserves - temp4_); } // converting into normal token amounts if (swap0to1_) { temp_ = ((temp_ * TOKEN_0_DENOMINATOR_PRECISION) / TOKEN_0_NUMERATOR_PRECISION); temp3_ = ((temp3_ * TOKEN_0_DENOMINATOR_PRECISION) / TOKEN_0_NUMERATOR_PRECISION); // only adding uncheck in out amount unchecked { temp2_ = ((temp2_ * TOKEN_1_DENOMINATOR_PRECISION) / TOKEN_1_NUMERATOR_PRECISION); temp4_ = ((temp4_ * TOKEN_1_DENOMINATOR_PRECISION) / TOKEN_1_NUMERATOR_PRECISION); } } else { temp_ = ((temp_ * TOKEN_1_DENOMINATOR_PRECISION) / TOKEN_1_NUMERATOR_PRECISION); temp3_ = ((temp3_ * TOKEN_1_DENOMINATOR_PRECISION) / TOKEN_1_NUMERATOR_PRECISION); // only adding uncheck in out amount unchecked { temp2_ = ((temp2_ * TOKEN_0_DENOMINATOR_PRECISION) / TOKEN_0_NUMERATOR_PRECISION); temp4_ = ((temp4_ * TOKEN_0_DENOMINATOR_PRECISION) / TOKEN_0_NUMERATOR_PRECISION); } } unchecked { amountOut_ = temp2_ + temp4_; } // if address dead then reverting with amountOut if (extras_.to == ADDRESS_DEAD) revert FluidDexSwapResult(amountOut_); if (amountOut_ < extras_.amountOutMin) revert FluidDexError(ErrorTypes.DexT1__NotEnoughAmountOut); // allocating to avoid stack-too-deep error // not setting in the callbackData as last 2nd to avoid SKIP_TRANSFERS clashing s_.data = abi.encode(amountIn_, extras_.isCallback, msg.sender); // true/false is to decide if dex should do callback or directly transfer from user // deposit & payback token in at liquidity LIQUIDITY.operate{ value: msg.value }(s_.tokenIn, int(temp_), -int(temp3_), address(0), address(0), s_.data); // withdraw & borrow token out at liquidity LIQUIDITY.operate(s_.tokenOut, -int(temp2_), int(temp4_), s_.withdrawTo, s_.borrowTo, new bytes(0)); // if hook exists then calling hook temp_ = (dexVariables2_ >> 142) & X30; if (temp_ > 0) { s_.swap0to1 = swap0to1_; _hookVerify(temp_, 1, s_.swap0to1, s_.price); } swap0to1_ ? _utilizationVerify(((dexVariables2_ >> 238) & X10), EXCHANGE_PRICE_TOKEN_1_SLOT) : _utilizationVerify(((dexVariables2_ >> 228) & X10), EXCHANGE_PRICE_TOKEN_0_SLOT); dexVariables = _updateOracle(s_.price, pex_.centerPrice, dexVariables_); emit Swap(swap0to1_, amountIn_, amountOut_, extras_.to); } /// @dev Swap tokens with perfect amount in /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0 /// @param amountIn_ The exact amount of tokens to swap in /// @param amountOutMin_ The minimum amount of tokens to receive after swap /// @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_ ) public payable returns (uint256 amountOut_) { return _swapIn(swap0to1_, amountIn_, SwapInExtras(to_, amountOutMin_, false)); } /// @dev Swap tokens with perfect amount in and callback functionality /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0 /// @param amountIn_ The exact amount of tokens to swap in /// @param amountOutMin_ The minimum amount of tokens to receive after swap /// @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 swapInWithCallback( bool swap0to1_, uint256 amountIn_, uint256 amountOutMin_, address to_ ) public payable returns (uint256 amountOut_) { return _swapIn(swap0to1_, amountIn_, SwapInExtras(to_, amountOutMin_, true)); } /// @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_ ) public payable returns (uint256 amountIn_) { return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256)); } /// @dev Swap tokens with perfect amount out and callback functionality /// @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 swapOutWithCallback( bool swap0to1_, uint256 amountOut_, uint256 amountInMax_, address to_ ) public payable returns (uint256 amountIn_) { return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256)); } /// @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_ ) public payable returns (uint token0Amt_, uint token1Amt_) { return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256, uint256)); } /// @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 withdrawn 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_ ) public returns (uint token0Amt_, uint token1Amt_) { return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256, uint256)); } /// @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 borrowed 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_ ) public returns (uint token0Amt_, uint token1Amt_) { return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256, uint256)); } /// @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_ ) public payable returns (uint token0Amt_, uint token1Amt_) { return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256, uint256)); } /// @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_ ) public payable returns (uint shares_) { return abi.decode(_spell(COL_OPERATIONS_IMPLEMENTATION, msg.data), (uint256)); } /// @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 withdrawn 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_ ) public returns (uint shares_) { return abi.decode(_spell(COL_OPERATIONS_IMPLEMENTATION, msg.data), (uint256)); } /// @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 borrowed 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_ ) public returns (uint shares_) { return abi.decode(_spell(DEBT_OPERATIONS_IMPLEMENTATION, msg.data), (uint256)); } /// @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_ ) public payable returns (uint shares_) { return abi.decode(_spell(DEBT_OPERATIONS_IMPLEMENTATION, msg.data), (uint256)); } /// @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 withdrawn 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_ ) public returns (uint withdrawAmt_) { return abi.decode(_spell(COL_OPERATIONS_IMPLEMENTATION, msg.data), (uint256)); } /// @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_ ) public payable returns (uint paybackAmt_) { return abi.decode(_spell(DEBT_OPERATIONS_IMPLEMENTATION, msg.data), (uint256)); } /// @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 FluidDexError(ErrorTypes.DexT1__MsgSenderNotLiquidity); if (dexVariables & 1 == 0) revert FluidDexError(ErrorTypes.DexT1__ReentrancyBitShouldBeOn); if (data_.length != 96) revert FluidDexError(ErrorTypes.DexT1__IncorrectDataLength); (uint amountToSend_, bool isCallback_, address from_) = abi.decode(data_, (uint, bool, address)); if (amountToSend_ < amount_) revert FluidDexError(ErrorTypes.DexT1__AmountToSendLessThanAmount); if (isCallback_) { IDexCallback(from_).dexCallback(token_, amountToSend_); } else { SafeTransfer.safeTransferFrom(token_, from_, address(LIQUIDITY), amountToSend_); } } /// @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_) { OraclePriceMemory memory o_; uint dexVariables_ = dexVariables; if ((dexVariables_ >> 195) & 1 == 0) { revert FluidDexError(ErrorTypes.DexT1__OracleNotActive); } twaps_ = new Oracle[](secondsAgos_.length); uint totalTime_; uint time_; uint i; uint secondsAgo_ = secondsAgos_[0]; currentPrice_ = (dexVariables_ >> 41) & X40; currentPrice_ = (currentPrice_ >> DEFAULT_EXPONENT_SIZE) << (currentPrice_ & DEFAULT_EXPONENT_MASK); uint price_ = currentPrice_; o_.lowestPrice1by0 = currentPrice_; o_.highestPrice1by0 = currentPrice_; uint twap1by0_; uint twap0by1_; uint j; o_.oracleSlot = (dexVariables_ >> 176) & X3; o_.oracleMap = (dexVariables_ >> 179) & X16; // if o_.oracleSlot == 7 then it'll enter the if statement in the below while loop o_.oracle = o_.oracleSlot < 7 ? _oracle[o_.oracleMap] : 0; uint slotData_; uint percentDiff_; if (((dexVariables_ >> 121) & X33) < block.timestamp) { // last swap didn't occured in this block. // hence last price is current price of pool & also the last price time_ = block.timestamp - ((dexVariables_ >> 121) & X33); } else { // last swap occured in this block, that means current price is active for 0 secs. Hence TWAP for it will be 0. ++j; } while (true) { if (j == 2) { if (++o_.oracleSlot == 8) { o_.oracleSlot = 0; if (o_.oracleMap == 0) { o_.oracleMap = TOTAL_ORACLE_MAPPING; } o_.oracle = _oracle[--o_.oracleMap]; } slotData_ = (o_.oracle >> (o_.oracleSlot * 32)) & X32; if (slotData_ > 0) { time_ = slotData_ & X9; if (time_ == 0) { // time is in precision & sign bits time_ = slotData_ >> 9; // if o_.oracleSlot is 7 then precision & bits and stored in 1 less map if (o_.oracleSlot == 7) { o_.oracleSlot = 0; if (o_.oracleMap == 0) { o_.oracleMap = TOTAL_ORACLE_MAPPING; } o_.oracle = _oracle[--o_.oracleMap]; slotData_ = o_.oracle & X32; } else { ++o_.oracleSlot; slotData_ = (o_.oracle >> (o_.oracleSlot * 32)) & X32; } } percentDiff_ = slotData_ >> 10; percentDiff_ = (ORACLE_LIMIT * percentDiff_) / X22; if (((slotData_ >> 9) & 1 == 1)) { // if positive then old price was lower than current hence subtracting price_ = price_ - (price_ * percentDiff_) / ORACLE_PRECISION; } else { // if negative then old price was higher than current hence adding price_ = price_ + (price_ * percentDiff_) / ORACLE_PRECISION; } } else { // oracle data does not exist. Probably due to pool recently got initialized and not have much swaps. revert FluidDexError(ErrorTypes.DexT1__InsufficientOracleData); } } else if (j == 1) { // last & last to last price price_ = (dexVariables_ >> 1) & X40; price_ = (price_ >> DEFAULT_EXPONENT_SIZE) << (price_ & DEFAULT_EXPONENT_MASK); time_ = (dexVariables_ >> 154) & X22; ++j; } else if (j == 0) { ++j; } totalTime_ += time_; if (o_.lowestPrice1by0 > price_) o_.lowestPrice1by0 = price_; if (o_.highestPrice1by0 < price_) o_.highestPrice1by0 = price_; if (totalTime_ < secondsAgo_) { twap1by0_ += price_ * time_; twap0by1_ += (1e54 / price_) * time_; } else { time_ = time_ + secondsAgo_ - totalTime_; twap1by0_ += price_ * time_; twap0by1_ += (1e54 / price_) * time_; // also auto checks that secondsAgos_ should not be == 0 twap1by0_ = twap1by0_ / secondsAgo_; twap0by1_ = twap0by1_ / secondsAgo_; twaps_[i] = Oracle( twap1by0_, o_.lowestPrice1by0, o_.highestPrice1by0, twap0by1_, (1e54 / o_.highestPrice1by0), (1e54 / o_.lowestPrice1by0) ); // TWAP for next secondsAgo will start with price_ o_.lowestPrice1by0 = price_; o_.highestPrice1by0 = price_; while (++i < secondsAgos_.length) { // secondsAgo_ = [60, 15, 0] time_ = totalTime_ - secondsAgo_; // updating total time as new seconds ago started totalTime_ = time_; // also auto checks that secondsAgos_[i + 1] > secondsAgos_[i] secondsAgo_ = secondsAgos_[i] - secondsAgos_[i - 1]; if (totalTime_ < secondsAgo_) { twap1by0_ = price_ * time_; twap0by1_ = (1e54 / price_) * time_; // if time_ comes out as 0 here then lowestPrice & highestPrice should not be price_, it should be next price_ that we will calculate if (time_ == 0) { o_.lowestPrice1by0 = type(uint).max; o_.highestPrice1by0 = 0; } break; } else { time_ = time_ + secondsAgo_ - totalTime_; // twap1by0_ = price_ here twap1by0_ = price_ * time_; // twap0by1_ = (1e54 / price_) * time_; twap0by1_ = (1e54 / price_) * time_; twap1by0_ = twap1by0_ / secondsAgo_; twap0by1_ = twap0by1_ / secondsAgo_; twaps_[i] = Oracle( twap1by0_, o_.lowestPrice1by0, o_.highestPrice1by0, twap0by1_, (1e54 / o_.highestPrice1by0), (1e54 / o_.lowestPrice1by0) ); } } if (i == secondsAgos_.length) return (twaps_, currentPrice_); // oracle fetch over } } } function getPricesAndExchangePrices() public { uint dexVariables_ = dexVariables; uint dexVariables2_ = dexVariables2; _check(dexVariables_, dexVariables2_); PricesAndExchangePrice memory pex_ = _getPricesAndExchangePrices(dexVariables, dexVariables2); revert FluidDexPricesAndExchangeRates(pex_); } /// @dev Internal fallback function to handle calls to non-existent functions /// @notice This function is called when a transaction is sent to the contract without matching any other function /// @notice It checks if the caller is authorized, enables re-entrancy protection, delegates the call to the admin implementation, and then disables re-entrancy protection /// @notice Only authorized callers (global or dex auth) can trigger this function /// @notice This function uses assembly to perform a delegatecall to the admin implementation to update configs related to DEX function _fallback() private { if (!(DEX_FACTORY.isGlobalAuth(msg.sender) || DEX_FACTORY.isDexAuth(address(this), msg.sender))) { revert FluidDexError(ErrorTypes.DexT1__NotAnAuth); } uint dexVariables_ = dexVariables; if (dexVariables_ & 1 == 1) revert FluidDexError(ErrorTypes.DexT1__AlreadyEntered); // enabling re-entrancy dexVariables = dexVariables_ | 1; // Delegate the current call to `ADMIN_IMPLEMENTATION`. _spell(ADMIN_IMPLEMENTATION, msg.data); // disabling re-entrancy // directly fetching from storage so updates from Admin module will get auto covered dexVariables = dexVariables & ~uint(1); } fallback() external payable { _fallback(); } receive() external payable { if (msg.sig != 0x00000000) { _fallback(); } } /// @notice returns all Vault constants function constantsView() external view returns (ConstantViews memory constantsView_) { constantsView_.dexId = DEX_ID; constantsView_.liquidity = address(LIQUIDITY); constantsView_.factory = address(DEX_FACTORY); constantsView_.token0 = TOKEN_0; constantsView_.token1 = TOKEN_1; constantsView_.implementations.shift = SHIFT_IMPLEMENTATION; constantsView_.implementations.admin = ADMIN_IMPLEMENTATION; constantsView_.implementations.colOperations = COL_OPERATIONS_IMPLEMENTATION; constantsView_.implementations.debtOperations = DEBT_OPERATIONS_IMPLEMENTATION; constantsView_.implementations.perfectOperationsAndSwapOut = PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION; constantsView_.deployerContract = DEPLOYER_CONTRACT; constantsView_.supplyToken0Slot = SUPPLY_TOKEN_0_SLOT; constantsView_.borrowToken0Slot = BORROW_TOKEN_0_SLOT; constantsView_.supplyToken1Slot = SUPPLY_TOKEN_1_SLOT; constantsView_.borrowToken1Slot = BORROW_TOKEN_1_SLOT; constantsView_.exchangePriceToken0Slot = EXCHANGE_PRICE_TOKEN_0_SLOT; constantsView_.exchangePriceToken1Slot = EXCHANGE_PRICE_TOKEN_1_SLOT; constantsView_.oracleMapping = TOTAL_ORACLE_MAPPING; } /// @notice returns all Vault constants function constantsView2() external view returns (ConstantViews2 memory constantsView2_) { constantsView2_.token0NumeratorPrecision = TOKEN_0_NUMERATOR_PRECISION; constantsView2_.token0DenominatorPrecision = TOKEN_0_DENOMINATOR_PRECISION; constantsView2_.token1NumeratorPrecision = TOKEN_1_NUMERATOR_PRECISION; constantsView2_.token1DenominatorPrecision = TOKEN_1_DENOMINATOR_PRECISION; } /// @notice Calculates the real and imaginary reserves for collateral tokens /// @dev This function retrieves the supply of both tokens from the liquidity layer, /// adjusts them based on exchange prices, and calculates imaginary reserves /// based on the geometric mean and price range /// @param geometricMean_ The geometric mean of the token prices /// @param upperRange_ The upper price range /// @param lowerRange_ The lower price range /// @param token0SupplyExchangePrice_ The exchange price for token0 from liquidity layer /// @param token1SupplyExchangePrice_ The exchange price for token1 from liquidity layer /// @return c_ A struct containing the calculated real and imaginary reserves for both tokens: /// - token0RealReserves: The real reserves of token0 /// - token1RealReserves: The real reserves of token1 /// - token0ImaginaryReserves: The imaginary reserves of token0 /// - token1ImaginaryReserves: The imaginary reserves of token1 function getCollateralReserves( uint geometricMean_, uint upperRange_, uint lowerRange_, uint token0SupplyExchangePrice_, uint token1SupplyExchangePrice_ ) public view returns (CollateralReserves memory c_) { return _getCollateralReserves( geometricMean_, upperRange_, lowerRange_, token0SupplyExchangePrice_, token1SupplyExchangePrice_ ); } /// @notice Calculates the debt reserves for both tokens /// @param geometricMean_ The geometric mean of the upper and lower price ranges /// @param upperRange_ The upper price range /// @param lowerRange_ The lower price range /// @param token0BorrowExchangePrice_ The exchange price of token0 from liquidity layer /// @param token1BorrowExchangePrice_ The exchange price of token1 from liquidity layer /// @return d_ The calculated debt reserves for both tokens, containing: /// - token0Debt: The debt amount of token0 /// - token1Debt: The debt amount of token1 /// - token0RealReserves: The real reserves of token0 derived from token1 debt /// - token1RealReserves: The real reserves of token1 derived from token0 debt /// - token0ImaginaryReserves: The imaginary debt reserves of token0 /// - token1ImaginaryReserves: The imaginary debt reserves of token1 function getDebtReserves( uint geometricMean_, uint upperRange_, uint lowerRange_, uint token0BorrowExchangePrice_, uint token1BorrowExchangePrice_ ) public view returns (DebtReserves memory d_) { return _getDebtReserves( geometricMean_, upperRange_, lowerRange_, token0BorrowExchangePrice_, token1BorrowExchangePrice_ ); } }
// 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"; import { DexSlotsLink } from "./dexSlotsLink.sol"; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // @DEV ATTENTION: ON ANY CHANGES HERE, MAKE SURE THAT LOGIC IN VAULTS WILL STILL BE VALID. // SOME CODE THERE ASSUMES DEXCALCS == LIQUIDITYCALCS. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! /// @notice implements calculation methods used for Fluid Dex such as updated withdrawal / borrow limits. library DexCalcs { // 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 X14 = 0x3fff; uint256 internal constant X18 = 0x3ffff; uint256 internal constant X24 = 0xffffff; uint256 internal constant X33 = 0x1ffffffff; uint256 internal constant X64 = 0xffffffffffffffff; /////////////////////////////////////////////////////////////////////////// ////////// 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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0 // extract last set borrow limit BigMathMinified.fromBigNumber( (userBorrowData_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_ >> DexSlotsLink.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_; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; /// @notice library that helps in reading / working with storage slot data of Fluid Dex. /// @dev as all data for Fluid Dex is internal, any data must be fetched directly through manual /// slot reading through this library or, if gas usage is less important, through the FluidDexResolver. library DexSlotsLink { /// @dev storage slot for variables at Dex uint256 internal constant DEX_VARIABLES_SLOT = 0; /// @dev storage slot for variables2 at Dex uint256 internal constant DEX_VARIABLES2_SLOT = 1; /// @dev storage slot for total supply shares at Dex uint256 internal constant DEX_TOTAL_SUPPLY_SHARES_SLOT = 2; /// @dev storage slot for user supply mapping at Dex uint256 internal constant DEX_USER_SUPPLY_MAPPING_SLOT = 3; /// @dev storage slot for total borrow shares at Dex uint256 internal constant DEX_TOTAL_BORROW_SHARES_SLOT = 4; /// @dev storage slot for user borrow mapping at Dex uint256 internal constant DEX_USER_BORROW_MAPPING_SLOT = 5; /// @dev storage slot for oracle mapping at Dex uint256 internal constant DEX_ORACLE_MAPPING_SLOT = 6; /// @dev storage slot for range and threshold shifts at Dex uint256 internal constant DEX_RANGE_THRESHOLD_SHIFTS_SLOT = 7; /// @dev storage slot for center price shift at Dex uint256 internal constant DEX_CENTER_PRICE_SHIFT_SLOT = 8; // -------------------------------- // @dev stacked uint256 storage slots bits position data for each: // UserSupplyData uint256 internal constant BITS_USER_SUPPLY_ALLOWED = 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; // UserBorrowData uint256 internal constant BITS_USER_BORROW_ALLOWED = 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; // -------------------------------- /// @notice Calculating the slot ID for Dex 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 Dex 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: 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; }
// 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; 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; import { Structs } from "./poolT1/coreModule/structs.sol"; abstract contract Error { error FluidDexError(uint256 errorId_); error FluidDexFactoryError(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(Structs.PricesAndExchangePrice pex_); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; library ErrorTypes { /***********************************| | DexT1 | |__________________________________*/ /// @notice thrown at reentrancy uint256 internal constant DexT1__AlreadyEntered = 51001; uint256 internal constant DexT1__NotAnAuth = 51002; uint256 internal constant DexT1__SmartColNotEnabled = 51003; uint256 internal constant DexT1__SmartDebtNotEnabled = 51004; uint256 internal constant DexT1__PoolNotInitialized = 51005; uint256 internal constant DexT1__TokenReservesTooLow = 51006; uint256 internal constant DexT1__EthAndAmountInMisMatch = 51007; uint256 internal constant DexT1__EthSentForNonNativeSwap = 51008; uint256 internal constant DexT1__NoSwapRoute = 51009; uint256 internal constant DexT1__NotEnoughAmountOut = 51010; uint256 internal constant DexT1__LiquidityLayerTokenUtilizationCapReached = 51011; uint256 internal constant DexT1__HookReturnedFalse = 51012; // Either user's config are not set or user is paused uint256 internal constant DexT1__UserSupplyInNotOn = 51013; // Either user's config are not set or user is paused uint256 internal constant DexT1__UserDebtInNotOn = 51014; // Thrown when contract asks for more token0 or token1 than what user's wants to give on deposit uint256 internal constant DexT1__AboveDepositMax = 51015; uint256 internal constant DexT1__MsgValueLowOnDepositOrPayback = 51016; uint256 internal constant DexT1__WithdrawLimitReached = 51017; // Thrown when contract gives less token0 or token1 than what user's wants on withdraw uint256 internal constant DexT1__BelowWithdrawMin = 51018; uint256 internal constant DexT1__DebtLimitReached = 51019; // Thrown when contract gives less token0 or token1 than what user's wants on borrow uint256 internal constant DexT1__BelowBorrowMin = 51020; // Thrown when contract asks for more token0 or token1 than what user's wants on payback uint256 internal constant DexT1__AbovePaybackMax = 51021; uint256 internal constant DexT1__InvalidDepositAmts = 51022; uint256 internal constant DexT1__DepositAmtsZero = 51023; uint256 internal constant DexT1__SharesMintedLess = 51024; uint256 internal constant DexT1__WithdrawalNotEnough = 51025; uint256 internal constant DexT1__InvalidWithdrawAmts = 51026; uint256 internal constant DexT1__WithdrawAmtsZero = 51027; uint256 internal constant DexT1__WithdrawExcessSharesBurn = 51028; uint256 internal constant DexT1__InvalidBorrowAmts = 51029; uint256 internal constant DexT1__BorrowAmtsZero = 51030; uint256 internal constant DexT1__BorrowExcessSharesMinted = 51031; uint256 internal constant DexT1__PaybackAmtTooHigh = 51032; uint256 internal constant DexT1__InvalidPaybackAmts = 51033; uint256 internal constant DexT1__PaybackAmtsZero = 51034; uint256 internal constant DexT1__PaybackSharedBurnedLess = 51035; uint256 internal constant DexT1__NothingToArbitrage = 51036; uint256 internal constant DexT1__MsgSenderNotLiquidity = 51037; // On liquidity callback reentrancy bit should be on uint256 internal constant DexT1__ReentrancyBitShouldBeOn = 51038; // Thrown is reentrancy is already on and someone tries to fetch oracle price. Should not be possible to this uint256 internal constant DexT1__OraclePriceFetchAlreadyEntered = 51039; // Thrown when swap changes the current price by more than 5% uint256 internal constant DexT1__OracleUpdateHugeSwapDiff = 51040; uint256 internal constant DexT1__Token0ShouldBeSmallerThanToken1 = 51041; uint256 internal constant DexT1__OracleMappingOverflow = 51042; /// @notice thrown if governance has paused the swapping & arbitrage so only perfect functions are usable uint256 internal constant DexT1__SwapAndArbitragePaused = 51043; uint256 internal constant DexT1__ExceedsAmountInMax = 51044; /// @notice thrown if amount in is too high or too low uint256 internal constant DexT1__SwapInLimitingAmounts = 51045; /// @notice thrown if amount out is too high or too low uint256 internal constant DexT1__SwapOutLimitingAmounts = 51046; uint256 internal constant DexT1__MintAmtOverflow = 51047; uint256 internal constant DexT1__BurnAmtOverflow = 51048; uint256 internal constant DexT1__LimitingAmountsSwapAndNonPerfectActions = 51049; uint256 internal constant DexT1__InsufficientOracleData = 51050; uint256 internal constant DexT1__SharesAmountInsufficient = 51051; uint256 internal constant DexT1__CenterPriceOutOfRange = 51052; uint256 internal constant DexT1__DebtReservesTooLow = 51053; uint256 internal constant DexT1__SwapAndDepositTooLowOrTooHigh = 51054; uint256 internal constant DexT1__WithdrawAndSwapTooLowOrTooHigh = 51055; uint256 internal constant DexT1__BorrowAndSwapTooLowOrTooHigh = 51056; uint256 internal constant DexT1__SwapAndPaybackTooLowOrTooHigh = 51057; uint256 internal constant DexT1__InvalidImplementation = 51058; uint256 internal constant DexT1__OnlyDelegateCallAllowed = 51059; uint256 internal constant DexT1__IncorrectDataLength = 51060; uint256 internal constant DexT1__AmountToSendLessThanAmount = 51061; uint256 internal constant DexT1__InvalidCollateralReserves = 51062; uint256 internal constant DexT1__InvalidDebtReserves = 51063; uint256 internal constant DexT1__SupplySharesOverflow = 51064; uint256 internal constant DexT1__BorrowSharesOverflow = 51065; uint256 internal constant DexT1__OracleNotActive = 51066; /***********************************| | DEX Admin | |__________________________________*/ /// @notice thrown when pool is not initialized uint256 internal constant DexT1Admin__PoolNotInitialized = 52001; uint256 internal constant DexT1Admin__SmartColIsAlreadyOn = 52002; uint256 internal constant DexT1Admin__SmartDebtIsAlreadyOn = 52003; /// @notice thrown when any of the configs value overflow the maximum limit uint256 internal constant DexT1Admin__ConfigOverflow = 52004; uint256 internal constant DexT1Admin__AddressNotAContract = 52005; uint256 internal constant DexT1Admin__InvalidParams = 52006; uint256 internal constant DexT1Admin__UserNotDefined = 52007; uint256 internal constant DexT1Admin__OnlyDelegateCallAllowed = 52008; uint256 internal constant DexT1Admin__UnexpectedPoolState = 52009; /// @notice thrown when trying to pause or unpause but user is already in the target pause state uint256 internal constant DexT1Admin__InvalidPauseToggle = 52009; /***********************************| | DEX Factory | |__________________________________*/ uint256 internal constant DexFactory__InvalidOperation = 53001; uint256 internal constant DexFactory__Unauthorized = 53002; uint256 internal constant DexFactory__SameTokenNotAllowed = 53003; uint256 internal constant DexFactory__TokenConfigNotProper = 53004; uint256 internal constant DexFactory__InvalidParams = 53005; uint256 internal constant DexFactory__OnlyDelegateCallAllowed = 53006; uint256 internal constant DexFactory__InvalidDexAddress = 53007; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; interface IFluidDexFactory { /// @notice Global auth is auth for all dexes function isGlobalAuth(address auth_) external view returns (bool); /// @notice Dex auth is auth for a specific dex function isDexAuth(address vault_, address auth_) external view returns (bool); /// @notice Total dexes deployed. function totalDexes() external view returns (uint256); /// @notice Compute dexAddress function getDexAddress(uint256 dexId_) 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: 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; import { StorageRead } from "../../../../libraries/storageRead.sol"; interface ITokenDecimals { function decimals() external view returns (uint8); } abstract contract ConstantVariables is StorageRead { /*////////////////////////////////////////////////////////////// CONSTANTS / IMMUTABLES //////////////////////////////////////////////////////////////*/ address internal constant TEAM_MULTISIG = 0x4F6F977aCDD1177DCD81aB83074855EcB9C2D49e; address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; uint256 internal constant NATIVE_TOKEN_DECIMALS = 18; address internal constant ADDRESS_DEAD = 0x000000000000000000000000000000000000dEaD; uint256 internal constant TOKENS_DECIMALS_PRECISION = 12; uint256 internal constant TOKENS_DECIMALS = 1e12; uint256 internal constant SMALL_COEFFICIENT_SIZE = 10; uint256 internal constant DEFAULT_COEFFICIENT_SIZE = 56; uint256 internal constant DEFAULT_EXPONENT_SIZE = 8; uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF; uint256 internal constant X2 = 0x3; uint256 internal constant X3 = 0x7; uint256 internal constant X5 = 0x1f; uint256 internal constant X7 = 0x7f; uint256 internal constant X8 = 0xff; uint256 internal constant X9 = 0x1ff; uint256 internal constant X10 = 0x3ff; uint256 internal constant X11 = 0x7ff; uint256 internal constant X14 = 0x3fff; uint256 internal constant X16 = 0xffff; uint256 internal constant X17 = 0x1ffff; uint256 internal constant X18 = 0x3ffff; uint256 internal constant X20 = 0xfffff; uint256 internal constant X22 = 0x3fffff; uint256 internal constant X23 = 0x7fffff; uint256 internal constant X24 = 0xffffff; uint256 internal constant X28 = 0xfffffff; uint256 internal constant X30 = 0x3fffffff; uint256 internal constant X32 = 0xffffffff; uint256 internal constant X33 = 0x1ffffffff; uint256 internal constant X40 = 0xffffffffff; uint256 internal constant X64 = 0xffffffffffffffff; uint256 internal constant X96 = 0xffffffffffffffffffffffff; uint256 internal constant X128 = 0xffffffffffffffffffffffffffffffff; uint256 internal constant TWO_DECIMALS = 1e2; uint256 internal constant THREE_DECIMALS = 1e3; uint256 internal constant FOUR_DECIMALS = 1e4; uint256 internal constant FIVE_DECIMALS = 1e5; uint256 internal constant SIX_DECIMALS = 1e6; uint256 internal constant EIGHT_DECIMALS = 1e8; uint256 internal constant NINE_DECIMALS = 1e9; uint256 internal constant PRICE_PRECISION = 1e27; uint256 internal constant ORACLE_PRECISION = 1e18; // 100% uint256 internal constant ORACLE_LIMIT = 5 * 1e16; // 5% /// after swap token0 reserves should not be less than token1InToken0 / MINIMUM_LIQUIDITY_SWAP /// after swap token1 reserves should not be less than token0InToken1 / MINIMUM_LIQUIDITY_SWAP uint256 internal constant MINIMUM_LIQUIDITY_SWAP = 1e4; /// after user operations (deposit, withdraw, borrow, payback) token0 reserves should not be less than token1InToken0 / MINIMUM_LIQUIDITY_USER_OPERATIONS /// after user operations (deposit, withdraw, borrow, payback) token1 reserves should not be less than token0InToken0 / MINIMUM_LIQUIDITY_USER_OPERATIONS uint256 internal constant MINIMUM_LIQUIDITY_USER_OPERATIONS = 1e6; /// To skip transfers in liquidity layer if token in & out is same and liquidity layer is on the winning side bytes32 internal constant SKIP_TRANSFERS = keccak256(bytes("SKIP_TRANSFERS")); function _decimals(address token_) internal view returns (uint256) { return (token_ == NATIVE_TOKEN) ? NATIVE_TOKEN_DECIMALS : ITokenDecimals(token_).decimals(); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; abstract contract Variables { /*////////////////////////////////////////////////////////////// STORAGE VARIABLES //////////////////////////////////////////////////////////////*/ /// First 1 bit => 0 => re-entrancy. If 0 then allow transaction to go, else throw. /// Next 40 bits => 1-40 => last to last stored price. BigNumber (32 bits precision, 8 bits exponent) /// Next 40 bits => 41-80 => last stored price of pool. BigNumber (32 bits precision, 8 bits exponent) /// Next 40 bits => 81-120 => center price. Center price from where the ranges will be calculated. BigNumber (32 bits precision, 8 bits exponent) /// Next 33 bits => 121-153 => last interaction time stamp /// Next 22 bits => 154-175 => max 4194303 seconds (~1165 hrs, ~48.5 days), time difference between last to last and last price stored /// Next 3 bits => 176-178 => oracle checkpoint, if 0 then first slot, if 7 then last slot /// Next 16 bits => 179-194 => current mapping or oracle, after every 8 transaction it will increase by 1. Max capacity is 65535 but it can be lower than that check dexVariables2 /// Next 1 bit => 195 => is oracle active? uint internal dexVariables; /// Next 1 bit => 0 => is smart collateral enabled? /// Next 1 bit => 1 => is smart debt enabled? /// Next 17 bits => 2-18 => fee (1% = 10000, max value: 100000 = 10%, fee should not be more than 10%) /// Next 7 bits => 19-25 => revenue cut from fee (1 = 1%, 100 = 100%). If fee is 1000 = 0.1% and revenue cut is 10 = 10% then governance get 0.01% of every swap /// Next 1 bit => 26 => percent active change going on or not, 0 = false, 1 = true, if true than that means governance has updated the below percents and the update should happen with a specified time. /// Next 20 bits => 27-46 => upperPercent (1% = 10000, max value: 104.8575%) upperRange - upperRange * upperPercent = centerPrice. Hence, upperRange = centerPrice / (1 - upperPercent) /// Next 20 bits => 47-66 => lowerPercent. lowerRange = centerPrice - centerPrice * lowerPercent. /// Next 1 bit => 67 => threshold percent active change going on or not, 0 = false, 1 = true, if true than that means governance has updated the below percents and the update should happen with a specified time. /// Next 10 bits => 68-77 => upper shift threshold percent, 1 = 0.1%. 1000 = 100%. if currentPrice > (centerPrice + (upperRange - centerPrice) * (1000 - upperShiftThresholdPercent) / 1000) then trigger shift /// Next 10 bits => 78-87 => lower shift threshold percent, 1 = 0.1%. 1000 = 100%. if currentPrice < (centerPrice - (centerPrice - lowerRange) * (1000 - lowerShiftThresholdPercent) / 1000) then trigger shift /// Next 24 bits => 88-111 => Shifting time (~194 days) (rate = (% up + % down) / time ?) /// Next 30 bits => 112-131 => Address of center price if center price should be fetched externally, for example, for wstETH <> ETH pool, fetch wstETH exchange rate into stETH from wstETH contract. /// Why fetch it externally? Because let's say pool width is 0.1% and wstETH temporarily got depeg of 0.5% then pool will start to shift to newer pricing /// but we don't want pool to shift to 0.5% because we know the depeg will recover so to avoid the loss for users. /// Next 30 bits => 142-171 => Hooks bits, calculate hook address by storing deployment nonce from factory. /// Next 28 bits => 172-199 => max center price. BigNumber (20 bits precision, 8 bits exponent) /// Next 28 bits => 200-227 => min center price. BigNumber (20 bits precision, 8 bits exponent) /// Next 10 bits => 228-237 => utilization limit of token0. Max value 1000 = 100%, if 100% then no need to check the utilization. /// Next 10 bits => 238-247 => utilization limit of token1. Max value 1000 = 100%, if 100% then no need to check the utilization. /// Next 1 bit => 248 => is center price shift active /// Last 1 bit => 255 => Pause swap & arbitrage (only perfect functions will be usable), if we need to pause entire DEX then that can be done through pausing DEX on Liquidity Layer uint internal dexVariables2; /// first 128 bits => 0-127 => total supply shares /// last 128 bits => 128-255 => max supply shares uint internal _totalSupplyShares; /// @dev user supply data: user -> data /// Aside from 1st bit, entire bits here are same as liquidity layer _userSupplyData. Hence exact same supply & borrow limit library can be used /// First 1 bit => 0 => is user allowed to supply? 0 = not allowed, 1 = allowed /// Next 64 bits => 1- 64 => user supply amount/shares; BigMath: 56 | 8 /// Next 64 bits => 65-128 => previous user withdrawal limit; BigMath: 56 | 8 /// Next 33 bits => 129-161 => last triggered process timestamp (enough until 16 March 2242 -> max value 8589934591) /// Next 14 bits => 162-175 => expand withdrawal limit percentage (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383). /// @dev shrinking is instant /// Next 24 bits => 176-199 => withdrawal limit expand duration in seconds.(Max value 16_777_215; ~4_660 hours, ~194 days) /// Next 18 bits => 200-217 => base withdrawal limit: below this, 100% withdrawals can be done (aka shares can be burned); BigMath: 10 | 8 /// Next 38 bits => 218-255 => empty for future use mapping(address => uint) internal _userSupplyData; /// first 128 bits => 0-127 => total borrow shares /// last 128 bits => 128-255 => max borrow shares uint internal _totalBorrowShares; /// @dev user borrow data: user -> data /// Aside from 1st bit, entire bits here are same as liquidity layer _userBorrowData. Hence exact same supply & borrow limit library function can be used /// First 1 bit => 0 => is user allowed to borrow? 0 = not allowed, 1 = allowed /// Next 64 bits => 1- 64 => user debt amount/shares; BigMath: 56 | 8 /// Next 64 bits => 65-128 => previous user debt ceiling; BigMath: 56 | 8 /// Next 33 bits => 129-161 => last triggered process timestamp (enough until 16 March 2242 -> max value 8589934591) /// Next 14 bits => 162-175 => expand debt ceiling percentage (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383) /// @dev shrinking is instant /// Next 24 bits => 176-199 => debt ceiling expand duration in seconds (Max value 16_777_215; ~4_660 hours, ~194 days) /// Next 18 bits => 200-217 => base debt ceiling: below this, there's no debt ceiling limits; BigMath: 10 | 8 /// Next 18 bits => 218-235 => max debt ceiling: absolute maximum debt ceiling can expand to; BigMath: 10 | 8 /// Next 20 bits => 236-255 => empty for future use mapping(address => uint) internal _userBorrowData; /// Price difference between last swap of last block & last swap of new block /// If last swap happened at Block B - 4 and next swap happened after 4 blocks at Block B then it will store that difference /// considering time difference between these 4 blocks is 48 seconds, hence time will be stored as 48 /// New oracle update: /// time to 9 bits and precision to 22 bits /// if time exceeds 9 bits which is 511 sec or ~8.5 min then we will use 2 oracle slot to store the data /// we will leave the both time slot as 0 and on first sign + precision slot we will store time and /// on second sign + precision slot we will store sign & precision /// First 9 bits => 0- 8 => time, 511 seconds /// Next 1 bit => 9 => sign of percent in change, if 1 then 0 or positive, else negative /// Next 22 bits => 10- 31 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746% /// Next 9 bits => 32- 40 => time, 511 seconds /// Next 1 bit => 41 => sign of percent in change, if 1 then 0 or positive, else negative /// Next 22 bits => 42- 63 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746% /// Next 9 bits => 64- 72 => time, 511 seconds /// Next 1 bit => 73 => sign of percent in change, if 1 then 0 or positive, else negative /// Next 22 bits => 74- 95 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746% /// Next 9 bits => 96-104 => time, 511 seconds /// Next 1 bit => 105 => sign of percent in change, if 1 then 0 or positive, else negative /// Next 22 bits => 106-127 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746% /// Next 9 bits => 128-136 => time, 511 seconds /// Next 1 bit => 137 => sign of percent in change, if 1 then 0 or positive, else negative /// Next 22 bits => 138-159 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746% /// Next 9 bits => 160-168 => time, 511 seconds /// Next 1 bit => 169 => sign of percent in change, if 1 then 0 or positive, else negative /// Next 22 bits => 170-191 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746% /// Next 9 bits => 192-200 => time, 511 seconds /// Next 1 bit => 201 => sign of percent in change, if 1 then 0 or positive, else negative /// Next 22 bits => 202-223 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746% /// Next 9 bits => 224-232 => time, 511 seconds /// Next 1 bit => 233 => sign of percent in change, if 1 then 0 or positive, else negative /// Next 22 bits => 234-255 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746% mapping(uint => uint) internal _oracle; /// First 20 bits => 0-19 => old upper shift /// Next 20 bits => 20-39 => old lower shift /// Next 20 bits => 40-59 => in seconds, ~12 days max, shift can last for max ~12 days /// Next 33 bits => 60-92 => timestamp of when the shift has started. uint128 internal _rangeShift; /// First 10 bits => 0- 9 => old upper shift /// Next 10 bits => 10-19 => empty so we can use same helper function /// Next 10 bits => 20-29 => old lower shift /// Next 10 bits => 30-39 => empty so we can use same helper function /// Next 20 bits => 40-59 => in seconds, ~12 days max, shift can last for max ~12 days /// Next 33 bits => 60-92 => timestamp of when the shift has started. /// Next 24 bits => 93-116 => old threshold time uint128 internal _thresholdShift; /// Shifting is fuzzy and with time it'll keep on getting closer and then eventually get over /// First 33 bits => 0 -32 => starting timestamp /// Next 20 bits => 33-52 => % shift /// Next 20 bits => 53-72 => time to shift that percent uint256 internal _centerPriceShift; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; abstract contract Events { /// @notice Emitted on token swaps /// @param swap0to1 Indicates whether the swap is from token0 to token1 or vice-versa. /// @param amountIn The amount of tokens to be sent to the vault to swap. /// @param amountOut The amount of tokens user got from the swap. /// @param to Recepient of swapped tokens. event Swap(bool swap0to1, uint256 amountIn, uint256 amountOut, address to); /// @notice Emitted when liquidity is added with shares specified. /// @param shares Expected exact shares to be received. /// @param token0Amt Amount of token0 deposited. /// @param token0Amt Amount of token1 deposited. event LogDepositPerfectColLiquidity(uint shares, uint token0Amt, uint token1Amt); /// @notice Emitted when liquidity is withdrawn with shares specified. /// @param shares shares burned /// @param token0Amt Amount of token0 withdrawn. /// @param token1Amt Amount of token1 withdrawn. event LogWithdrawPerfectColLiquidity(uint shares, uint token0Amt, uint token1Amt); /// @notice Emitted when liquidity is borrowed with shares specified. /// @param shares shares minted /// @param token0Amt Amount of token0 borrowed. /// @param token1Amt Amount of token1 borrowed. event LogBorrowPerfectDebtLiquidity(uint shares, uint token0Amt, uint token1Amt); /// @notice Emitted when liquidity is paid back with shares specified. /// @param shares shares burned /// @param token0Amt Amount of token0 paid back. /// @param token1Amt Amount of token1 paid back. event LogPaybackPerfectDebtLiquidity(uint shares, uint token0Amt, uint token1Amt); /// @notice Emitted when liquidity is deposited with specified token0 & token1 amount /// @param amount0 Amount of token0 deposited. /// @param amount1 Amount of token1 deposited. /// @param shares Amount of shares minted. event LogDepositColLiquidity(uint amount0, uint amount1, uint shares); /// @notice Emitted when liquidity is withdrawn with specified token0 & token1 amount /// @param amount0 Amount of token0 withdrawn. /// @param amount1 Amount of token1 withdrawn. /// @param shares Amount of shares burned. event LogWithdrawColLiquidity(uint amount0, uint amount1, uint shares); /// @notice Emitted when liquidity is borrowed with specified token0 & token1 amount /// @param amount0 Amount of token0 borrowed. /// @param amount1 Amount of token1 borrowed. /// @param shares Amount of shares minted. event LogBorrowDebtLiquidity(uint amount0, uint amount1, uint shares); /// @notice Emitted when liquidity is paid back with specified token0 & token1 amount /// @param amount0 Amount of token0 paid back. /// @param amount1 Amount of token1 paid back. /// @param shares Amount of shares burned. event LogPaybackDebtLiquidity(uint amount0, uint amount1, uint shares); /// @notice Emitted when liquidity is withdrawn with shares specified into one token only. /// @param shares shares burned /// @param token0Amt Amount of token0 withdrawn. /// @param token1Amt Amount of token1 withdrawn. event LogWithdrawColInOneToken(uint shares, uint token0Amt, uint token1Amt); /// @notice Emitted when liquidity is paid back with shares specified from one token only. /// @param shares shares burned /// @param token0Amt Amount of token0 paid back. /// @param token1Amt Amount of token1 paid back. event LogPaybackDebtInOneToken(uint shares, uint token0Amt, uint token1Amt); /// @notice Emitted when internal arbitrage between 2 pools happen /// @param routing if positive then routing is amtIn of token0 in deposit & borrow else token0 withdraw & payback /// @param amtOut if routing is positive then token1 withdraw & payback amount else token1 deposit & borrow event LogArbitrage(int routing, uint amtOut); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { FixedPointMathLib } from "solmate/src/utils/FixedPointMathLib.sol"; import { Variables } from "../../common/variables.sol"; import { ImmutableVariables } from "../immutableVariables.sol"; import { Events } from "../events.sol"; import { ErrorTypes } from "../../../errorTypes.sol"; import { IHook, ICenterPrice } from "../interfaces.sol"; import { LiquiditySlotsLink } from "../../../../../libraries/liquiditySlotsLink.sol"; import { LiquidityCalcs } from "../../../../../libraries/liquidityCalcs.sol"; import { DexSlotsLink } from "../../../../../libraries/dexSlotsLink.sol"; import { DexCalcs } from "../../../../../libraries/dexCalcs.sol"; import { BigMathMinified } from "../../../../../libraries/bigMathMinified.sol"; import { AddressCalcs } from "../../../../../libraries/addressCalcs.sol"; interface IShifting { /// @dev Calculates the new upper and lower range values during an active range shift /// @param upperRange_ The target upper range value /// @param lowerRange_ The target lower range value /// @param dexVariables2_ needed in case shift is ended and we need to update dexVariables2 /// @return The updated upper range, lower range, and dexVariables2 function _calcRangeShifting( uint upperRange_, uint lowerRange_, uint dexVariables2_ ) external payable returns (uint, uint, uint); /// @dev Calculates the new threshold values during an active threshold shift /// @param upperThreshold_ The target upper threshold value /// @param lowerThreshold_ The target lower threshold value /// @param dexVariables2_ needed in case shift is ended and we need to update dexVariables2 /// @return The updated upper threshold, lower threshold, and dexVariables2 function _calcThresholdShifting( uint upperThreshold_, uint lowerThreshold_, uint dexVariables2_ ) external payable returns (uint, uint, uint); /// @dev Calculates the new center price during an active center price shift /// @param dexVariables_ The current state of dex variables /// @param dexVariables2_ Additional dex variables /// @return The updated center price function _calcCenterPrice( uint dexVariables_, uint dexVariables2_ ) external payable returns (uint); } abstract contract CoreHelpers is Variables, ImmutableVariables, Events { using BigMathMinified for uint256; /// @dev do any arbitrary call /// @param target_ Address to which the call needs to be delegated /// @param data_ Data to execute at the delegated address 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) if iszero(succeeded) { // throw if delegatecall failed returndatacopy(0x00, 0x00, size) revert(0x00, size) } } } /// @dev Given an input amount of asset and pair reserves, returns the maximum output amount of the other asset /// @param amountIn_ The amount of input asset. /// @param iReserveIn_ Imaginary token reserve with input amount. /// @param iReserveOut_ Imaginary token reserve of output amount. function _getAmountOut( uint256 amountIn_, uint iReserveIn_, uint iReserveOut_ ) internal pure returns (uint256 amountOut_) { unchecked { // Both numerator and denominator are scaled to 1e6 to factor in fee scaling. uint256 numerator_ = amountIn_ * iReserveOut_; uint256 denominator_ = iReserveIn_ + amountIn_; // Using the swap formula: (AmountIn * iReserveY) / (iReserveX + AmountIn) amountOut_ = numerator_ / denominator_; } } /// @dev Given an output amount of asset and pair reserves, returns the input amount of the other asset /// @param amountOut_ Desired output amount of the asset. /// @param iReserveIn_ Imaginary token reserve of input amount. /// @param iReserveOut_ Imaginary token reserve of output amount. function _getAmountIn( uint256 amountOut_, uint iReserveIn_, uint iReserveOut_ ) internal pure returns (uint256 amountIn_) { // Both numerator and denominator are scaled to 1e6 to factor in fee scaling. uint256 numerator_ = amountOut_ * iReserveIn_; uint256 denominator_ = iReserveOut_ - amountOut_; // Using the swap formula: (AmountOut * iReserveX) / (iReserveY - AmountOut) amountIn_ = numerator_ / denominator_; } /// @param t total amount in /// @param x imaginary reserves of token out of collateral /// @param y imaginary reserves of token in of collateral /// @param x2 imaginary reserves of token out of debt /// @param y2 imaginary reserves of token in of debt /// @return a_ how much swap should go through collateral pool. Remaining will go from debt /// note if a < 0 then entire trade route through debt pool and debt pool arbitrage with col pool /// note if a > t then entire trade route through col pool and col pool arbitrage with debt pool /// note if a > 0 & a < t then swap will route through both pools function _swapRoutingIn(uint t, uint x, uint y, uint x2, uint y2) internal pure returns (int a_) { // Main equations: // 1. out = x * a / (y + a) // 2. out2 = x2 * (t - a) / (y2 + (t - a)) // final price should be same // 3. (y + a) / (x - out) = (y2 + (t - a)) / (x2 - out2) // derivation: https://chatgpt.com/share/dce6f381-ee5f-4d5f-b6ea-5996e84d5b57 // adding 1e18 precision uint xyRoot_ = FixedPointMathLib.sqrt(x * y * 1e18); uint x2y2Root_ = FixedPointMathLib.sqrt(x2 * y2 * 1e18); a_ = (int(y2 * xyRoot_ + t * xyRoot_) - int(y * x2y2Root_)) / int(xyRoot_ + x2y2Root_); } /// @param t total amount out /// @param x imaginary reserves of token in of collateral /// @param y imaginary reserves of token out of collateral /// @param x2 imaginary reserves of token in of debt /// @param y2 imaginary reserves of token out of debt /// @return a_ how much swap should go through collateral pool. Remaining will go from debt /// note if a < 0 then entire trade route through debt pool and debt pool arbitrage with col pool /// note if a > t then entire trade route through col pool and col pool arbitrage with debt pool /// note if a > 0 & a < t then swap will route through both pools function _swapRoutingOut(uint t, uint x, uint y, uint x2, uint y2) internal pure returns (int a_) { // Main equations: // 1. in = (x * a) / (y - a) // 2. in2 = (x2 * (t - a)) / (y2 - (t - a)) // final price should be same // 3. (y - a) / (x + in) = (y2 - (t - a)) / (x2 + in2) // derivation: https://chatgpt.com/share/6585bc28-841f-49ec-aea2-1e5c5b7f4fa9 // adding 1e18 precision uint xyRoot_ = FixedPointMathLib.sqrt(x * y * 1e18); uint x2y2Root_ = FixedPointMathLib.sqrt(x2 * y2 * 1e18); // 1e18 precision gets cancelled out in division a_ = (int(t * xyRoot_ + y * x2y2Root_) - int(y2 * xyRoot_)) / int(xyRoot_ + x2y2Root_); } function _utilizationVerify(uint utilizationLimit_, bytes32 exchangePriceSlot_) internal view { if (utilizationLimit_ < THREE_DECIMALS) { utilizationLimit_ = utilizationLimit_ * 10; // extracting utilization of token from liquidity layer uint liquidityLayerUtilization_ = LIQUIDITY.readFromStorage(exchangePriceSlot_); liquidityLayerUtilization_ = (liquidityLayerUtilization_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14; // Note: this can go slightly above the utilization limit if no update is written to storage at liquidity layer // if swap was not big enough to go far enough above or any other storage update threshold write cause there // so just to keep in mind when configuring the actual limit reachable can be utilizationLimit_ + storageUpdateThreshold at Liquidity if (liquidityLayerUtilization_ > utilizationLimit_) revert FluidDexError(ErrorTypes.DexT1__LiquidityLayerTokenUtilizationCapReached); } } function _check(uint dexVariables_, uint dexVariables2_) internal { if (dexVariables_ & 1 == 1) revert FluidDexError(ErrorTypes.DexT1__AlreadyEntered); if (dexVariables2_ & 3 == 0) revert FluidDexError(ErrorTypes.DexT1__PoolNotInitialized); // enabling re-entrancy dexVariables = dexVariables_ | 1; } /// @dev if token0 reserves are too low w.r.t token1 then revert, this is to avoid edge case scenario and making sure that precision on calculations should be high enough function _verifyToken0Reserves( uint token0Reserves_, uint token1Reserves_, uint centerPrice_, uint minLiquidity_ ) internal pure { if (((token0Reserves_) < ((token1Reserves_ * 1e27) / (centerPrice_ * minLiquidity_)))) { revert FluidDexError(ErrorTypes.DexT1__TokenReservesTooLow); } } /// @dev if token1 reserves are too low w.r.t token0 then revert, this is to avoid edge case scenario and making sure that precision on calculations should be high enough function _verifyToken1Reserves( uint token0Reserves_, uint token1Reserves_, uint centerPrice_, uint minLiquidity_ ) internal pure { if (((token1Reserves_) < ((token0Reserves_ * centerPrice_) / (1e27 * minLiquidity_)))) { revert FluidDexError(ErrorTypes.DexT1__TokenReservesTooLow); } } function _verifySwapAndNonPerfectActions(uint amountAdjusted_, uint amount_) internal pure { // after shifting amount should not become 0 // limiting to six decimals which means in case of USDC, USDT it's 1 wei, for WBTC 100 wei, for ETH 1000 gwei if (amountAdjusted_ < SIX_DECIMALS || amountAdjusted_ > X96 || amount_ < TWO_DECIMALS || amount_ > X128) revert FluidDexError(ErrorTypes.DexT1__LimitingAmountsSwapAndNonPerfectActions); } /// @dev Calculates the new upper and lower range values during an active range shift /// @param upperRange_ The target upper range value /// @param lowerRange_ The target lower range value /// @param dexVariables2_ needed in case shift is ended and we need to update dexVariables2 /// @return The updated upper range, lower range, and dexVariables2 /// @notice This function handles the gradual shifting of range values over time /// @notice If the shift is complete, it updates the state and clears the shift data function _calcRangeShifting( uint upperRange_, uint lowerRange_, uint dexVariables2_ ) internal returns (uint, uint, uint) { return abi.decode( _spell( SHIFT_IMPLEMENTATION, abi.encodeWithSelector( IShifting._calcRangeShifting.selector, upperRange_, lowerRange_, dexVariables2_ ) ), (uint, uint, uint) ); } /// @dev Calculates the new upper and lower threshold values during an active threshold shift /// @param upperThreshold_ The target upper threshold value /// @param lowerThreshold_ The target lower threshold value /// @param thresholdTime_ The time passed since shifting started /// @return The updated upper threshold, lower threshold, and threshold time /// @notice This function handles the gradual shifting of threshold values over time /// @notice If the shift is complete, it updates the state and clears the shift data function _calcThresholdShifting( uint upperThreshold_, uint lowerThreshold_, uint thresholdTime_ ) internal returns (uint, uint, uint) { return abi.decode( _spell( SHIFT_IMPLEMENTATION, abi.encodeWithSelector( IShifting._calcThresholdShifting.selector, upperThreshold_, lowerThreshold_, thresholdTime_ ) ), (uint, uint, uint) ); } /// @dev Calculates the new center price during an active price shift /// @param dexVariables_ The current state of dex variables /// @param dexVariables2_ Additional dex variables /// @return newCenterPrice_ The updated center price /// @notice This function gradually shifts the center price towards a new target price over time /// @notice It uses an external price source (via ICenterPrice) to determine the target price /// @notice The shift continues until the current price reaches the target, or the shift duration ends /// @notice Once the shift is complete, it updates the state and clears the shift data /// @notice The shift rate is dynamic and depends on: /// @notice - Time remaining in the shift duration /// @notice - The new center price (fetched externally, which may change) /// @notice - The current (old) center price /// @notice This results in a fuzzy shifting mechanism where the rate can change as these parameters evolve /// @notice The externally fetched new center price is expected to not differ significantly from the last externally fetched center price function _calcCenterPrice(uint dexVariables_, uint dexVariables2_) internal returns (uint newCenterPrice_) { return abi.decode( _spell( SHIFT_IMPLEMENTATION, abi.encodeWithSelector(IShifting._calcCenterPrice.selector, dexVariables_, dexVariables2_) ), (uint) ); } /// @notice Calculates and returns the current prices and exchange prices for the pool /// @param dexVariables_ The first set of DEX variables containing various pool parameters /// @param dexVariables2_ The second set of DEX variables containing additional pool parameters /// @return pex_ A struct containing the calculated prices and exchange prices: /// - pex_.lastStoredPrice: The last stored price in 1e27 decimals /// - pex_.centerPrice: The calculated or fetched center price in 1e27 decimals /// - pex_.upperRange: The upper range price limit in 1e27 decimals /// - pex_.lowerRange: The lower range price limit in 1e27 decimals /// - pex_.geometricMean: The geometric mean of upper range & lower range in 1e27 decimals /// - pex_.supplyToken0ExchangePrice: The current exchange price for supplying token0 /// - pex_.borrowToken0ExchangePrice: The current exchange price for borrowing token0 /// - pex_.supplyToken1ExchangePrice: The current exchange price for supplying token1 /// - pex_.borrowToken1ExchangePrice: The current exchange price for borrowing token1 /// @dev This function performs the following operations: /// 1. Determines the center price (either from storage, external source, or calculated) /// 2. Retrieves the last stored price from dexVariables_ /// 3. Calculates the upper and lower range prices based on the center price and range percentages /// 4. Checks if rebalancing is needed based on threshold settings /// 5. Adjusts prices if necessary based on the time elapsed and threshold conditions /// 6. Update the dexVariables2_ if changes were made /// 7. Returns the calculated prices and exchange prices in the PricesAndExchangePrice struct function _getPricesAndExchangePrices( uint dexVariables_, uint dexVariables2_ ) internal returns (PricesAndExchangePrice memory pex_) { uint centerPrice_; if (((dexVariables2_ >> 248) & 1) == 0) { // centerPrice_ => center price hook centerPrice_ = (dexVariables2_ >> 112) & X30; if (centerPrice_ == 0) { centerPrice_ = (dexVariables_ >> 81) & X40; centerPrice_ = (centerPrice_ >> DEFAULT_EXPONENT_SIZE) << (centerPrice_ & DEFAULT_EXPONENT_MASK); } else { // center price should be fetched from external source. For exmaple, in case of wstETH <> ETH pool, // we would want the center price to be pegged to wstETH exchange rate into ETH centerPrice_ = ICenterPrice(AddressCalcs.addressCalc(DEPLOYER_CONTRACT, centerPrice_)).centerPrice(); } } else { // an active centerPrice_ shift is going on centerPrice_ = _calcCenterPrice(dexVariables_, dexVariables2_); } uint lastStoredPrice_ = (dexVariables_ >> 41) & X40; lastStoredPrice_ = (lastStoredPrice_ >> DEFAULT_EXPONENT_SIZE) << (lastStoredPrice_ & DEFAULT_EXPONENT_MASK); uint upperRange_ = ((dexVariables2_ >> 27) & X20); uint lowerRange_ = ((dexVariables2_ >> 47) & X20); if (((dexVariables2_ >> 26) & 1) == 1) { // an active range shift is going on (upperRange_, lowerRange_, dexVariables2_) = _calcRangeShifting(upperRange_, lowerRange_, dexVariables2_); } unchecked { // adding into unchecked because upperRange_ & lowerRange_ can only be > 0 & < SIX_DECIMALS // 1% = 1e4, 100% = 1e6 upperRange_ = (centerPrice_ * SIX_DECIMALS) / (SIX_DECIMALS - upperRange_); // 1% = 1e4, 100% = 1e6 lowerRange_ = (centerPrice_ * (SIX_DECIMALS - lowerRange_)) / SIX_DECIMALS; } bool changed_; { // goal will be to keep threshold percents 0 if center price is fetched from external source // checking if threshold are set non 0 then only rebalancing is on if (((dexVariables2_ >> 68) & X20) > 0) { uint upperThreshold_ = (dexVariables2_ >> 68) & X10; uint lowerThreshold_ = (dexVariables2_ >> 78) & X10; uint shiftingTime_ = (dexVariables2_ >> 88) & X24; if (((dexVariables2_ >> 67) & 1) == 1) { // if active shift is going on for threshold then calculate threshold real time (upperThreshold_, lowerThreshold_, shiftingTime_) = _calcThresholdShifting( upperThreshold_, lowerThreshold_, shiftingTime_ ); } unchecked { if ( lastStoredPrice_ > (centerPrice_ + ((upperRange_ - centerPrice_) * (THREE_DECIMALS - upperThreshold_)) / THREE_DECIMALS) ) { uint timeElapsed_ = block.timestamp - ((dexVariables_ >> 121) & X33); // price shifting towards upper range if (timeElapsed_ < shiftingTime_) { centerPrice_ = centerPrice_ + ((upperRange_ - centerPrice_) * timeElapsed_) / shiftingTime_; } else { // 100% price shifted centerPrice_ = upperRange_; } changed_ = true; } else if ( lastStoredPrice_ < (centerPrice_ - ((centerPrice_ - lowerRange_) * (THREE_DECIMALS - lowerThreshold_)) / THREE_DECIMALS) ) { uint timeElapsed_ = block.timestamp - ((dexVariables_ >> 121) & X33); // price shifting towards lower range if (timeElapsed_ < shiftingTime_) { centerPrice_ = centerPrice_ - ((centerPrice_ - lowerRange_) * timeElapsed_) / shiftingTime_; } else { // 100% price shifted centerPrice_ = lowerRange_; } changed_ = true; } } } } // temp_ => max center price uint temp_ = (dexVariables2_ >> 172) & X28; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); if (centerPrice_ > temp_) { // if center price is greater than max center price centerPrice_ = temp_; changed_ = true; } else { // check if center price is less than min center price // temp_ => min center price temp_ = (dexVariables2_ >> 200) & X28; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); if (centerPrice_ < temp_) { centerPrice_ = temp_; changed_ = true; } } // if centerPrice_ is changed then calculating upper and lower range again if (changed_) { upperRange_ = ((dexVariables2_ >> 27) & X20); lowerRange_ = ((dexVariables2_ >> 47) & X20); if (((dexVariables2_ >> 26) & 1) == 1) { (upperRange_, lowerRange_, dexVariables2_) = _calcRangeShifting( upperRange_, lowerRange_, dexVariables2_ ); } unchecked { // adding into unchecked because upperRange_ & lowerRange_ can only be > 0 & < SIX_DECIMALS // 1% = 1e4, 100% = 1e6 upperRange_ = (centerPrice_ * SIX_DECIMALS) / (SIX_DECIMALS - upperRange_); // 1% = 1e4, 100% = 1e6 lowerRange_ = (centerPrice_ * (SIX_DECIMALS - lowerRange_)) / SIX_DECIMALS; } } pex_.lastStoredPrice = lastStoredPrice_; pex_.centerPrice = centerPrice_; pex_.upperRange = upperRange_; pex_.lowerRange = lowerRange_; unchecked { if (upperRange_ < 1e38) { // 1e38 * 1e38 = 1e76 which is less than max uint limit pex_.geometricMean = FixedPointMathLib.sqrt(upperRange_ * lowerRange_); } else { // upperRange_ price is pretty large hence lowerRange_ will also be pretty large pex_.geometricMean = FixedPointMathLib.sqrt((upperRange_ / 1e18) * (lowerRange_ / 1e18)) * 1e18; } } // Exchange price will remain same as Liquidity Layer (pex_.supplyToken0ExchangePrice, pex_.borrowToken0ExchangePrice) = LiquidityCalcs.calcExchangePrices( LIQUIDITY.readFromStorage(EXCHANGE_PRICE_TOKEN_0_SLOT) ); (pex_.supplyToken1ExchangePrice, pex_.borrowToken1ExchangePrice) = LiquidityCalcs.calcExchangePrices( LIQUIDITY.readFromStorage(EXCHANGE_PRICE_TOKEN_1_SLOT) ); } /// @dev getting reserves outside range. /// @param gp_ is geometric mean pricing of upper percent & lower percent /// @param pa_ price of upper range or lower range /// @param rx_ real reserves of token0 or token1 /// @param ry_ whatever is rx_ the other will be ry_ function _calculateReservesOutsideRange( uint gp_, uint pa_, uint rx_, uint ry_ ) internal pure returns (uint xa_, uint yb_) { // equations we have: // 1. x*y = k // 2. xa*ya = k // 3. xb*yb = k // 4. Pa = ya / xa = upperRange_ (known) // 5. Pb = yb / xb = lowerRange_ (known) // 6. x - xa = rx = real reserve of x (known) // 7. y - yb = ry = real reserve of y (known) // With solving we get: // ((Pa*Pb)^(1/2) - Pa)*xa^2 + (rx * (Pa*Pb)^(1/2) + ry)*xa + rx*ry = 0 // yb = yb = xa * (Pa * Pb)^(1/2) // xa = (GP⋅rx + ry + (-rx⋅ry⋅4⋅(GP - Pa) + (GP⋅rx + ry)^2)^0.5) / (2Pa - 2GP) // multiply entire equation by 1e27 to remove the price decimals precision of 1e27 // xa = (GP⋅rx + ry⋅1e27 + (rx⋅ry⋅4⋅(Pa - GP)⋅1e27 + (GP⋅rx + ry⋅1e27)^2)^0.5) / 2*(Pa - GP) // dividing the equation with 2*(Pa - GP). Pa is always > GP so answer will be positive. // xa = (((GP⋅rx + ry⋅1e27) / 2*(Pa - GP)) + (((rx⋅ry⋅4⋅(Pa - GP)⋅1e27) / 4*(Pa - GP)^2) + ((GP⋅rx + ry⋅1e27) / 2*(Pa - GP))^2)^0.5) // xa = (((GP⋅rx + ry⋅1e27) / 2*(Pa - GP)) + (((rx⋅ry⋅1e27) / (Pa - GP)) + ((GP⋅rx + ry⋅1e27) / 2*(Pa - GP))^2)^0.5) // dividing in 3 parts for simplification: // part1 = (Pa - GP) // part2 = (GP⋅rx + ry⋅1e27) / (2*part1) // part3 = rx⋅ry // note: part1 will almost always be < 1e28 but in case it goes above 1e27 then it's extremely unlikely it'll go above > 1e29 uint p1_ = pa_ - gp_; uint p2_ = ((gp_ * rx_) + (ry_ * 1e27)) / (2 * p1_); uint p3_ = rx_ * ry_; // to avoid overflowing p3_ = (p3_ < 1e50) ? ((p3_ * 1e27) / p1_) : (p3_ / p1_) * 1e27; // xa = part2 + (part3 + (part2 * part2))^(1/2) // yb = xa_ * gp_ xa_ = p2_ + FixedPointMathLib.sqrt((p3_ + (p2_ * p2_))); yb_ = (xa_ * gp_) / 1e27; } /// @dev Retrieves collateral amount from liquidity layer for a given token /// @param supplyTokenSlot_ The storage slot for the supply token data /// @param tokenExchangePrice_ The exchange price of the token /// @param isToken0_ Boolean indicating if the token is token0 (true) or token1 (false) /// @return tokenSupply_ The calculated liquidity collateral amount function _getLiquidityCollateral( bytes32 supplyTokenSlot_, uint tokenExchangePrice_, bool isToken0_ ) internal view returns (uint tokenSupply_) { uint tokenSupplyData_ = LIQUIDITY.readFromStorage(supplyTokenSlot_); tokenSupply_ = (tokenSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64; tokenSupply_ = (tokenSupply_ >> DEFAULT_EXPONENT_SIZE) << (tokenSupply_ & DEFAULT_EXPONENT_MASK); if (tokenSupplyData_ & 1 == 1) { // supply with interest is on unchecked { tokenSupply_ = (tokenSupply_ * tokenExchangePrice_) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION; } } unchecked { tokenSupply_ = isToken0_ ? ((tokenSupply_ * TOKEN_0_NUMERATOR_PRECISION) / TOKEN_0_DENOMINATOR_PRECISION) : ((tokenSupply_ * TOKEN_1_NUMERATOR_PRECISION) / TOKEN_1_DENOMINATOR_PRECISION); } } /// @notice Calculates the real and imaginary reserves for collateral tokens /// @dev This function retrieves the supply of both tokens from the liquidity layer, /// adjusts them based on exchange prices, and calculates imaginary reserves /// based on the geometric mean and price range /// @param geometricMean_ The geometric mean of the token prices /// @param upperRange_ The upper price range /// @param lowerRange_ The lower price range /// @param token0SupplyExchangePrice_ The exchange price for token0 from liquidity layer /// @param token1SupplyExchangePrice_ The exchange price for token1 from liquidity layer /// @return c_ A struct containing the calculated real and imaginary reserves for both tokens: /// - token0RealReserves: The real reserves of token0 /// - token1RealReserves: The real reserves of token1 /// - token0ImaginaryReserves: The imaginary reserves of token0 /// - token1ImaginaryReserves: The imaginary reserves of token1 function _getCollateralReserves( uint geometricMean_, uint upperRange_, uint lowerRange_, uint token0SupplyExchangePrice_, uint token1SupplyExchangePrice_ ) internal view returns (CollateralReserves memory c_) { uint token0Supply_ = _getLiquidityCollateral(SUPPLY_TOKEN_0_SLOT, token0SupplyExchangePrice_, true); uint token1Supply_ = _getLiquidityCollateral(SUPPLY_TOKEN_1_SLOT, token1SupplyExchangePrice_, false); if (geometricMean_ < 1e27) { (c_.token0ImaginaryReserves, c_.token1ImaginaryReserves) = _calculateReservesOutsideRange( geometricMean_, upperRange_, token0Supply_, token1Supply_ ); } else { // inversing, something like `xy = k` so for calculation we are making everything related to x into y & y into x // 1 / geometricMean for new geometricMean // 1 / lowerRange will become upper range // 1 / upperRange will become lower range (c_.token1ImaginaryReserves, c_.token0ImaginaryReserves) = _calculateReservesOutsideRange( (1e54 / geometricMean_), (1e54 / lowerRange_), token1Supply_, token0Supply_ ); } c_.token0RealReserves = token0Supply_; c_.token1RealReserves = token1Supply_; unchecked { c_.token0ImaginaryReserves += token0Supply_; c_.token1ImaginaryReserves += token1Supply_; } } /// @notice Calculates the real and imaginary debt reserves for both tokens /// @dev This function uses a quadratic equation to determine the debt reserves /// based on the geometric mean price and the current debt amounts /// @param gp_ The geometric mean price of upper range & lower range /// @param pb_ The price of lower range /// @param dx_ The debt amount of one token /// @param dy_ The debt amount of the other token /// @return rx_ The real debt reserve of the first token /// @return ry_ The real debt reserve of the second token /// @return irx_ The imaginary debt reserve of the first token /// @return iry_ The imaginary debt reserve of the second token function _calculateDebtReserves( uint gp_, uint pb_, uint dx_, uint dy_ ) internal pure returns (uint rx_, uint ry_, uint irx_, uint iry_) { // Assigning letter to knowns: // c = debtA // d = debtB // e = upperPrice // f = lowerPrice // g = upperPrice^1/2 // h = lowerPrice^1/2 // c = 1 // d = 2000 // e = 2222.222222 // f = 1800 // g = 2222.222222^1/2 // h = 1800^1/2 // Assigning letter to unknowns: // w = realDebtReserveA // x = realDebtReserveB // y = imaginaryDebtReserveA // z = imaginaryDebtReserveB // k = k // below quadratic will give answer of realDebtReserveB // A, B, C of quadratic equation: // A = h // B = dh - cfg // C = -cfdh // A = lowerPrice^1/2 // B = debtB⋅lowerPrice^1/2 - debtA⋅lowerPrice⋅upperPrice^1/2 // C = -(debtA⋅lowerPrice⋅debtB⋅lowerPrice^1/2) // x = (cfg − dh + (4cdf(h^2)+(cfg−dh)^2))^(1/2)) / 2h // simplifying dividing by h, note h = f^1/2 // x = ((c⋅g⋅(f^1/2) − d) / 2 + ((4⋅c⋅d⋅f⋅f) / (4h^2) + ((c⋅f⋅g) / 2h − (d⋅h) / 2h)^2))^(1/2)) // x = ((c⋅g⋅(f^1/2) − d) / 2 + ((c⋅d⋅f) + ((c⋅g⋅(f^1/2) − d) / 2)^2))^(1/2)) // dividing in 3 parts for simplification: // part1 = (c⋅g⋅(f^1/2) − d) / 2 // part2 = (c⋅d⋅f) // x = (part1 + (part2 + part1^2)^(1/2)) // note: part1 will almost always be < 1e27 but in case it goes above 1e27 then it's extremely unlikely it'll go above > 1e28 // part1 = ((debtA * upperPrice^1/2 * lowerPrice^1/2) - debtB) / 2 // note: upperPrice^1/2 * lowerPrice^1/2 = geometric mean // part1 = ((debtA * geometricMean) - debtB) / 2 // part2 = debtA * debtB * lowerPrice // converting decimals properly as price is in 1e27 decimals // part1 = ((debtA * geometricMean) - (debtB * 1e27)) / (2 * 1e27) // part2 = (debtA * debtB * lowerPrice) / 1e27 // final x equals: // x = (part1 + (part2 + part1^2)^(1/2)) int p1_ = (int(dx_ * gp_) - int(dy_ * 1e27)) / (2 * 1e27); uint p2_ = (dx_ * dy_); p2_ = p2_ < 1e50 ? (p2_ * pb_) / 1e27 : (p2_ / 1e27) * pb_; ry_ = uint(p1_ + int(FixedPointMathLib.sqrt((p2_ + uint(p1_ * p1_))))); // finding z: // x^2 - zx + cfz = 0 // z*(x - cf) = x^2 // z = x^2 / (x - cf) // z = x^2 / (x - debtA * lowerPrice) // converting decimals properly as price is in 1e27 decimals // z = (x^2 * 1e27) / ((x * 1e27) - (debtA * lowerPrice)) iry_ = ((ry_ * 1e27) - (dx_ * pb_)); if (iry_ < SIX_DECIMALS) { // almost impossible situation to ever get here revert FluidDexError(ErrorTypes.DexT1__DebtReservesTooLow); } if (ry_ < 1e25) { iry_ = (ry_ * ry_ * 1e27) / iry_; } else { // note: it can never result in negative as final result will always be in positive iry_ = (ry_ * ry_) / (iry_ / 1e27); } // finding y // x = z * c / (y + c) // y + c = z * c / x // y = (z * c / x) - c // y = (z * debtA / x) - debtA irx_ = ((iry_ * dx_) / ry_) - dx_; // finding w // w = y * d / (z + d) // w = (y * debtB) / (z + debtB) rx_ = (irx_ * dy_) / (iry_ + dy_); } /// @notice Calculates the debt amount for a given token from liquidity layer /// @param borrowTokenSlot_ The storage slot for the token's borrow data /// @param tokenExchangePrice_ The current exchange price of the token /// @param isToken0_ Boolean indicating if this is for token0 (true) or token1 (false) /// @return tokenDebt_ The calculated debt amount for the token function _getLiquidityDebt( bytes32 borrowTokenSlot_, uint tokenExchangePrice_, bool isToken0_ ) internal view returns (uint tokenDebt_) { uint tokenBorrowData_ = LIQUIDITY.readFromStorage(borrowTokenSlot_); tokenDebt_ = (tokenBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64; tokenDebt_ = (tokenDebt_ >> 8) << (tokenDebt_ & X8); if (tokenBorrowData_ & 1 == 1) { // borrow with interest is on unchecked { tokenDebt_ = (tokenDebt_ * tokenExchangePrice_) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION; } } unchecked { tokenDebt_ = isToken0_ ? ((tokenDebt_ * TOKEN_0_NUMERATOR_PRECISION) / TOKEN_0_DENOMINATOR_PRECISION) : ((tokenDebt_ * TOKEN_1_NUMERATOR_PRECISION) / TOKEN_1_DENOMINATOR_PRECISION); } } /// @notice Calculates the debt reserves for both tokens /// @param geometricMean_ The geometric mean of the upper and lower price ranges /// @param upperRange_ The upper price range /// @param lowerRange_ The lower price range /// @param token0BorrowExchangePrice_ The exchange price of token0 from liquidity layer /// @param token1BorrowExchangePrice_ The exchange price of token1 from liquidity layer /// @return d_ The calculated debt reserves for both tokens, containing: /// - token0Debt: The debt amount of token0 /// - token1Debt: The debt amount of token1 /// - token0RealReserves: The real reserves of token0 derived from token1 debt /// - token1RealReserves: The real reserves of token1 derived from token0 debt /// - token0ImaginaryReserves: The imaginary debt reserves of token0 /// - token1ImaginaryReserves: The imaginary debt reserves of token1 function _getDebtReserves( uint geometricMean_, uint upperRange_, uint lowerRange_, uint token0BorrowExchangePrice_, uint token1BorrowExchangePrice_ ) internal view returns (DebtReserves memory d_) { uint token0Debt_ = _getLiquidityDebt(BORROW_TOKEN_0_SLOT, token0BorrowExchangePrice_, true); uint token1Debt_ = _getLiquidityDebt(BORROW_TOKEN_1_SLOT, token1BorrowExchangePrice_, false); d_.token0Debt = token0Debt_; d_.token1Debt = token1Debt_; if (geometricMean_ < 1e27) { ( d_.token0RealReserves, d_.token1RealReserves, d_.token0ImaginaryReserves, d_.token1ImaginaryReserves ) = _calculateDebtReserves(geometricMean_, lowerRange_, token0Debt_, token1Debt_); } else { // inversing, something like `xy = k` so for calculation we are making everything related to x into y & y into x // 1 / geometricMean for new geometricMean // 1 / lowerRange will become upper range // 1 / upperRange will become lower range ( d_.token1RealReserves, d_.token0RealReserves, d_.token1ImaginaryReserves, d_.token0ImaginaryReserves ) = _calculateDebtReserves((1e54 / geometricMean_), (1e54 / upperRange_), token1Debt_, token0Debt_); } } function _priceDiffCheck(uint oldPrice_, uint newPrice_) internal pure returns (int priceDiff_) { // check newPrice_ & oldPrice_ difference should not be more than 5% // old price w.r.t new price priceDiff_ = int(ORACLE_PRECISION) - int((oldPrice_ * ORACLE_PRECISION) / newPrice_); unchecked { if ((priceDiff_ > int(ORACLE_LIMIT)) || (priceDiff_ < -int(ORACLE_LIMIT))) { // if oracle price difference is more than 5% then revert // in 1 swap price should only change by <= 5% // if a total fall by let's say 8% then in current block price can only fall by 5% and // in next block it'll fall the remaining 3% revert FluidDexError(ErrorTypes.DexT1__OracleUpdateHugeSwapDiff); } } } function _updateOracle(uint newPrice_, uint centerPrice_, uint dexVariables_) internal returns (uint) { // time difference between last & current swap uint timeDiff_ = block.timestamp - ((dexVariables_ >> 121) & X33); uint temp_; uint temp2_; uint temp3_; if (timeDiff_ == 0) { // doesn't matter if oracle is on or off when timediff = 0 code for both is same // temp_ => oldCenterPrice temp_ = (dexVariables_ >> 81) & X40; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); // Ensure that the center price is within the acceptable range of the old center price if it's not the first swap in the same block unchecked { if ( (centerPrice_ < (((EIGHT_DECIMALS - 1) * temp_) / EIGHT_DECIMALS)) || (centerPrice_ > (((EIGHT_DECIMALS + 1) * temp_) / EIGHT_DECIMALS)) ) { revert FluidDexError(ErrorTypes.DexT1__CenterPriceOutOfRange); } } // olderPrice_ => temp_ temp_ = (dexVariables_ >> 1) & X40; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); _priceDiffCheck(temp_, newPrice_); // 2nd swap in same block no need to update anything around oracle, only need to update last swap price in dexVariables return ((dexVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffe0000000001ffffffffff) | (newPrice_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 41)); } if (((dexVariables_ >> 195) & 1) == 0) { // if oracle is not active then just returning updated DEX variable temp_ = ((dexVariables_ >> 41) & X40); temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); _priceDiffCheck(temp_, newPrice_); return ((dexVariables_ & 0xfffffffffffffffffffffffffc00000000000000000000000000000000000001) | (((dexVariables_ >> 41) & X40) << 1) | (newPrice_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 41) | (centerPrice_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 81) | (block.timestamp << 121)); } else { // oracle is active hence update oracle // olderPrice_ => temp_ temp_ = (dexVariables_ >> 1) & X40; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); // oldPrice_ => temp2_ temp2_ = (dexVariables_ >> 41) & X40; temp2_ = (temp2_ >> DEFAULT_EXPONENT_SIZE) << (temp2_ & DEFAULT_EXPONENT_MASK); int priceDiff_ = _priceDiffCheck(temp2_, newPrice_); unchecked { // older price w.r.t old price priceDiff_ = int(ORACLE_PRECISION) - int((temp_ * ORACLE_PRECISION) / temp2_); } // priceDiffInPercentAndSign_ => temp3_ // priceDiff_ will always be lower than ORACLE_LIMIT due to above check unchecked { if (priceDiff_ < 0) { temp3_ = ((uint(-priceDiff_) * X22) / ORACLE_LIMIT) << 1; } else { // if greater than or equal to 0 then make sign flag 1 temp3_ = (((uint(priceDiff_) * X22) / ORACLE_LIMIT) << 1) | 1; } } if (timeDiff_ > X22) { // if time difference is this then that means DEX has been inactive ~45 days // that means oracle price of this DEX should not be used. timeDiff_ = X22; } // temp_ => lastTimeDiff_ temp_ = (dexVariables_ >> 154) & X22; uint nextOracleSlot_ = ((dexVariables_ >> 176) & X3); uint oracleMap_ = (dexVariables_ >> 179) & X16; if (temp_ > X9) { if (nextOracleSlot_ > 0) { // if greater than 0 then current slot has 2 or more oracle slot empty // First 9 bits are of time, so not using that temp3_ = (temp3_ << 41) | (temp_ << 9); _oracle[oracleMap_] = _oracle[oracleMap_] | (temp3_ << (--nextOracleSlot_ * 32)); if (nextOracleSlot_ > 0) { --nextOracleSlot_; } else { // if == 0 that means the oracle slots will get filled and shift to next oracle map nextOracleSlot_ = 7; unchecked { oracleMap_ = (oracleMap_ + 1) % TOTAL_ORACLE_MAPPING; } _oracle[oracleMap_] = 0; } } else { // if == 0 // then seconds will be in last map // precision will be in last map + 1 // Storing precision & sign slot in first precision & sign slot and leaving time slot empty temp3_ = temp3_ << 9; _oracle[oracleMap_] = _oracle[oracleMap_] | temp3_; nextOracleSlot_ = 6; // storing 6 here as 7 is going to occupied right now unchecked { oracleMap_ = (oracleMap_ + 1) % TOTAL_ORACLE_MAPPING; } // Storing time in 2nd precision & sign and leaving time slot empty _oracle[oracleMap_] = temp_ << ((7 * 32) + 9); } } else { temp3_ = (temp3_ << 9) | temp_; unchecked { if (nextOracleSlot_ < 7) { _oracle[oracleMap_] = _oracle[oracleMap_] | (temp3_ << (nextOracleSlot_ * 32)); } else { _oracle[oracleMap_] = temp3_ << ((7 * 32)); } } if (nextOracleSlot_ > 0) { --nextOracleSlot_; } else { nextOracleSlot_ = 7; unchecked { oracleMap_ = (oracleMap_ + 1) % TOTAL_ORACLE_MAPPING; } _oracle[oracleMap_] = 0; } } // doing this due to stack too deep error when using params memory variables temp_ = newPrice_; temp2_ = centerPrice_; temp3_ = dexVariables_; // then update last price return ((temp3_ & 0xfffffffffffffff8000000000000000000000000000000000000000000000001) | (((temp3_ >> 41) & X40) << 1) | (temp_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 41) | (temp2_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 81) | (block.timestamp << 121) | (timeDiff_ << 154) | (nextOracleSlot_ << 176) | (oracleMap_ << 179)); } } function _hookVerify(uint hookAddress_, uint mode_, bool swap0to1_, uint price_) internal { try IHook(AddressCalcs.addressCalc(DEPLOYER_CONTRACT, hookAddress_)).dexPrice( mode_, swap0to1_, TOKEN_0, TOKEN_1, price_ ) returns (bool isOk_) { if (!isOk_) revert FluidDexError(ErrorTypes.DexT1__HookReturnedFalse); } catch (bytes memory /*lowLevelData*/) { // skip checking hook nothing } } function _updateSupplyShares(uint newTotalShares_) internal { uint totalSupplyShares_ = _totalSupplyShares; // new total shares are greater than old total shares && new total shares are greater than max supply shares if ( (newTotalShares_ > (totalSupplyShares_ & X128)) && newTotalShares_ > (totalSupplyShares_ >> 128) ) { revert FluidDexError(ErrorTypes.DexT1__SupplySharesOverflow); } // keeping max supply shares intact _totalSupplyShares = ((totalSupplyShares_ >> 128) << 128) | newTotalShares_; } function _updateBorrowShares(uint newTotalShares_) internal { uint totalBorrowShares_ = _totalBorrowShares; // new total shares are greater than old total shares && new total shares are greater than max borrow shares if ( (newTotalShares_ > (totalBorrowShares_ & X128)) && newTotalShares_ > (totalBorrowShares_ >> 128) ) { revert FluidDexError(ErrorTypes.DexT1__BorrowSharesOverflow); } // keeping max borrow shares intact _totalBorrowShares = ((totalBorrowShares_ >> 128) << 128) | newTotalShares_; } constructor(ConstantViews memory constantViews_) ImmutableVariables(constantViews_) {} }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { IFluidLiquidity } from "../../../../liquidity/interfaces/iLiquidity.sol"; import { Structs } from "./structs.sol"; import { ConstantVariables } from "../common/constantVariables.sol"; import { IFluidDexFactory } from "../../interfaces/iDexFactory.sol"; import { Error } from "../../error.sol"; import { ErrorTypes } from "../../errorTypes.sol"; abstract contract ImmutableVariables is ConstantVariables, Structs, Error { /*////////////////////////////////////////////////////////////// CONSTANTS / IMMUTABLES //////////////////////////////////////////////////////////////*/ uint256 public immutable DEX_ID; /// @dev Address of token 0 address internal immutable TOKEN_0; /// @dev Address of token 1 address internal immutable TOKEN_1; address internal immutable THIS_CONTRACT; uint256 internal immutable TOKEN_0_NUMERATOR_PRECISION; uint256 internal immutable TOKEN_0_DENOMINATOR_PRECISION; uint256 internal immutable TOKEN_1_NUMERATOR_PRECISION; uint256 internal immutable TOKEN_1_DENOMINATOR_PRECISION; /// @dev Address of liquidity contract IFluidLiquidity internal immutable LIQUIDITY; /// @dev Address of DEX factory contract IFluidDexFactory internal immutable DEX_FACTORY; /// @dev Address of Shift implementation address internal immutable SHIFT_IMPLEMENTATION; /// @dev Address of Admin implementation address internal immutable ADMIN_IMPLEMENTATION; /// @dev Address of Col Operations implementation address internal immutable COL_OPERATIONS_IMPLEMENTATION; /// @dev Address of Debt Operations implementation address internal immutable DEBT_OPERATIONS_IMPLEMENTATION; /// @dev Address of Perfect Operations and Swap Out implementation address internal immutable PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION; /// @dev Address of contract used for deploying center price & hook related contract address internal immutable DEPLOYER_CONTRACT; /// @dev Liquidity layer slots bytes32 internal immutable SUPPLY_TOKEN_0_SLOT; bytes32 internal immutable BORROW_TOKEN_0_SLOT; bytes32 internal immutable SUPPLY_TOKEN_1_SLOT; bytes32 internal immutable BORROW_TOKEN_1_SLOT; bytes32 internal immutable EXCHANGE_PRICE_TOKEN_0_SLOT; bytes32 internal immutable EXCHANGE_PRICE_TOKEN_1_SLOT; uint256 internal immutable TOTAL_ORACLE_MAPPING; function _calcNumeratorAndDenominator( address token_ ) private view returns (uint256 numerator_, uint256 denominator_) { uint256 decimals_ = _decimals(token_); if (decimals_ > TOKENS_DECIMALS_PRECISION) { numerator_ = 1; denominator_ = 10 ** (decimals_ - TOKENS_DECIMALS_PRECISION); } else { numerator_ = 10 ** (TOKENS_DECIMALS_PRECISION - decimals_); denominator_ = 1; } } constructor(ConstantViews memory constants_) { THIS_CONTRACT = address(this); DEX_ID = constants_.dexId; LIQUIDITY = IFluidLiquidity(constants_.liquidity); DEX_FACTORY = IFluidDexFactory(constants_.factory); TOKEN_0 = constants_.token0; TOKEN_1 = constants_.token1; if (TOKEN_0 >= TOKEN_1) revert FluidDexError(ErrorTypes.DexT1__Token0ShouldBeSmallerThanToken1); (TOKEN_0_NUMERATOR_PRECISION, TOKEN_0_DENOMINATOR_PRECISION) = _calcNumeratorAndDenominator(TOKEN_0); (TOKEN_1_NUMERATOR_PRECISION, TOKEN_1_DENOMINATOR_PRECISION) = _calcNumeratorAndDenominator(TOKEN_1); if (constants_.implementations.shift != address(0)) { SHIFT_IMPLEMENTATION = constants_.implementations.shift; } else { SHIFT_IMPLEMENTATION = address(this); } if (constants_.implementations.admin != address(0)) { ADMIN_IMPLEMENTATION = constants_.implementations.admin; } else { ADMIN_IMPLEMENTATION = address(this); } if (constants_.implementations.colOperations != address(0)) { COL_OPERATIONS_IMPLEMENTATION = constants_.implementations.colOperations; } else { COL_OPERATIONS_IMPLEMENTATION = address(this); } if (constants_.implementations.debtOperations != address(0)) { DEBT_OPERATIONS_IMPLEMENTATION = constants_.implementations.debtOperations; } else { DEBT_OPERATIONS_IMPLEMENTATION = address(this); } if (constants_.implementations.perfectOperationsAndSwapOut != address(0)) { PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION = constants_.implementations.perfectOperationsAndSwapOut; } else { PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION = address(this); } DEPLOYER_CONTRACT = constants_.deployerContract; SUPPLY_TOKEN_0_SLOT = constants_.supplyToken0Slot; BORROW_TOKEN_0_SLOT = constants_.borrowToken0Slot; SUPPLY_TOKEN_1_SLOT = constants_.supplyToken1Slot; BORROW_TOKEN_1_SLOT = constants_.borrowToken1Slot; EXCHANGE_PRICE_TOKEN_0_SLOT = constants_.exchangePriceToken0Slot; EXCHANGE_PRICE_TOKEN_1_SLOT = constants_.exchangePriceToken1Slot; if (constants_.oracleMapping > X16) revert FluidDexError(ErrorTypes.DexT1__OracleMappingOverflow); TOTAL_ORACLE_MAPPING = constants_.oracleMapping; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; interface IHook { /// @notice Hook function to check for liquidation opportunities before external swaps /// @dev The primary use of this hook is to check if a particular pair vault has liquidation available. /// If liquidation is available, it gives priority to the liquidation process before allowing external swaps. /// In most cases, this hook will not be set. /// @param id_ Identifier for the operation type: 1 for swap, 2 for internal arbitrage /// @param swap0to1_ Direction of the swap: true if swapping token0 for token1, false otherwise /// @param token0_ Address of the first token in the pair /// @param token1_ Address of the second token in the pair /// @param price_ The price ratio of token1 to token0, expressed with 27 decimal places /// @return isOk_ Boolean indicating whether the operation should proceed function dexPrice( uint id_, bool swap0to1_, address token0_, address token1_, uint price_ ) external returns (bool isOk_); } interface ICenterPrice { /// @notice Retrieves the center price for the pool /// @dev This function is marked as non-constant (potentially state-changing) to allow flexibility in price fetching mechanisms. /// While typically used as a read-only operation, this design permits write operations if needed for certain token pairs /// (e.g., fetching up-to-date exchange rates that may require state changes). /// @return price The current price ratio of token1 to token0, expressed with 27 decimal places function centerPrice() external returns (uint price); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; abstract contract Structs { 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 ExchangePrices { uint supplyToken0ExchangePrice; uint borrowToken0ExchangePrice; uint supplyToken1ExchangePrice; uint borrowToken1ExchangePrice; } struct CollateralReserves { uint token0RealReserves; uint token1RealReserves; uint token0ImaginaryReserves; uint token1ImaginaryReserves; } struct CollateralReservesSwap { uint tokenInRealReserves; uint tokenOutRealReserves; uint tokenInImaginaryReserves; uint tokenOutImaginaryReserves; } struct DebtReserves { uint token0Debt; uint token1Debt; uint token0RealReserves; uint token1RealReserves; uint token0ImaginaryReserves; uint token1ImaginaryReserves; } struct DebtReservesSwap { uint tokenInDebt; uint tokenOutDebt; uint tokenInRealReserves; uint tokenOutRealReserves; uint tokenInImaginaryReserves; uint tokenOutImaginaryReserves; } struct SwapInMemory { address tokenIn; address tokenOut; uint256 amtInAdjusted; address withdrawTo; address borrowTo; uint price; // price of pool after swap uint fee; // fee of pool uint revenueCut; // revenue cut of pool bool swap0to1; int swapRoutingAmt; bytes data; // just added to avoid stack-too-deep error } struct SwapOutMemory { address tokenIn; address tokenOut; uint256 amtOutAdjusted; address withdrawTo; address borrowTo; uint price; // price of pool after swap uint fee; uint revenueCut; // revenue cut of pool bool swap0to1; int swapRoutingAmt; bytes data; // just added to avoid stack-too-deep error uint msgValue; } struct DepositColMemory { uint256 token0AmtAdjusted; uint256 token1AmtAdjusted; uint256 token0ReservesInitial; uint256 token1ReservesInitial; } struct WithdrawColMemory { uint256 token0AmtAdjusted; uint256 token1AmtAdjusted; uint256 token0ReservesInitial; uint256 token1ReservesInitial; address to; } struct BorrowDebtMemory { uint256 token0AmtAdjusted; uint256 token1AmtAdjusted; uint256 token0DebtInitial; uint256 token1DebtInitial; address to; } struct PaybackDebtMemory { uint256 token0AmtAdjusted; uint256 token1AmtAdjusted; uint256 token0DebtInitial; uint256 token1DebtInitial; } struct OraclePriceMemory { uint lowestPrice1by0; uint highestPrice1by0; uint oracleSlot; uint oracleMap; uint oracle; } 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 } struct Implementations { address shift; address admin; address colOperations; address debtOperations; address perfectOperationsAndSwapOut; } 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; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant MAX_UINT256 = 2**256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } }
{ "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
[{"inputs":[{"components":[{"internalType":"uint256","name":"dexId","type":"uint256"},{"internalType":"address","name":"liquidity","type":"address"},{"internalType":"address","name":"factory","type":"address"},{"components":[{"internalType":"address","name":"shift","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"colOperations","type":"address"},{"internalType":"address","name":"debtOperations","type":"address"},{"internalType":"address","name":"perfectOperationsAndSwapOut","type":"address"}],"internalType":"struct Structs.Implementations","name":"implementations","type":"tuple"},{"internalType":"address","name":"deployerContract","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"bytes32","name":"supplyToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"borrowToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"supplyToken1Slot","type":"bytes32"},{"internalType":"bytes32","name":"borrowToken1Slot","type":"bytes32"},{"internalType":"bytes32","name":"exchangePriceToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"exchangePriceToken1Slot","type":"bytes32"},{"internalType":"uint256","name":"oracleMapping","type":"uint256"}],"internalType":"struct Structs.ConstantViews","name":"constantViews_","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidDexError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId","type":"uint256"}],"name":"FluidDexFactoryError","type":"error"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"FluidDexLiquidityOutput","type":"error"},{"inputs":[{"internalType":"uint256","name":"token0Amt","type":"uint256"},{"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"FluidDexPerfectLiquidityOutput","type":"error"},{"inputs":[{"components":[{"internalType":"uint256","name":"lastStoredPrice","type":"uint256"},{"internalType":"uint256","name":"centerPrice","type":"uint256"},{"internalType":"uint256","name":"upperRange","type":"uint256"},{"internalType":"uint256","name":"lowerRange","type":"uint256"},{"internalType":"uint256","name":"geometricMean","type":"uint256"},{"internalType":"uint256","name":"supplyToken0ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"borrowToken0ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"supplyToken1ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"borrowToken1ExchangePrice","type":"uint256"}],"internalType":"struct Structs.PricesAndExchangePrice","name":"pex_","type":"tuple"}],"name":"FluidDexPricesAndExchangeRates","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenAmt","type":"uint256"}],"name":"FluidDexSingleTokenOutput","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"FluidDexSwapResult","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidLiquidityCalcsError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSafeTransferError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"routing","type":"int256"},{"indexed":false,"internalType":"uint256","name":"amtOut","type":"uint256"}],"name":"LogArbitrage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"LogBorrowDebtLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogBorrowPerfectDebtLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"LogDepositColLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogDepositPerfectColLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogPaybackDebtInOneToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"LogPaybackDebtLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogPaybackPerfectDebtLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogWithdrawColInOneToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"LogWithdrawColLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogWithdrawPerfectColLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"swap0to1","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"DEX_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"maxSharesAmt_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"minToken0Borrow_","type":"uint256"},{"internalType":"uint256","name":"minToken1Borrow_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"borrowPerfect","outputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"constantsView","outputs":[{"components":[{"internalType":"uint256","name":"dexId","type":"uint256"},{"internalType":"address","name":"liquidity","type":"address"},{"internalType":"address","name":"factory","type":"address"},{"components":[{"internalType":"address","name":"shift","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"colOperations","type":"address"},{"internalType":"address","name":"debtOperations","type":"address"},{"internalType":"address","name":"perfectOperationsAndSwapOut","type":"address"}],"internalType":"struct Structs.Implementations","name":"implementations","type":"tuple"},{"internalType":"address","name":"deployerContract","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"bytes32","name":"supplyToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"borrowToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"supplyToken1Slot","type":"bytes32"},{"internalType":"bytes32","name":"borrowToken1Slot","type":"bytes32"},{"internalType":"bytes32","name":"exchangePriceToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"exchangePriceToken1Slot","type":"bytes32"},{"internalType":"uint256","name":"oracleMapping","type":"uint256"}],"internalType":"struct Structs.ConstantViews","name":"constantsView_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"constantsView2","outputs":[{"components":[{"internalType":"uint256","name":"token0NumeratorPrecision","type":"uint256"},{"internalType":"uint256","name":"token0DenominatorPrecision","type":"uint256"},{"internalType":"uint256","name":"token1NumeratorPrecision","type":"uint256"},{"internalType":"uint256","name":"token1DenominatorPrecision","type":"uint256"}],"internalType":"struct Structs.ConstantViews2","name":"constantsView2_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"minSharesAmt_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"maxToken0Deposit_","type":"uint256"},{"internalType":"uint256","name":"maxToken1Deposit_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"depositPerfect","outputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"geometricMean_","type":"uint256"},{"internalType":"uint256","name":"upperRange_","type":"uint256"},{"internalType":"uint256","name":"lowerRange_","type":"uint256"},{"internalType":"uint256","name":"token0SupplyExchangePrice_","type":"uint256"},{"internalType":"uint256","name":"token1SupplyExchangePrice_","type":"uint256"}],"name":"getCollateralReserves","outputs":[{"components":[{"internalType":"uint256","name":"token0RealReserves","type":"uint256"},{"internalType":"uint256","name":"token1RealReserves","type":"uint256"},{"internalType":"uint256","name":"token0ImaginaryReserves","type":"uint256"},{"internalType":"uint256","name":"token1ImaginaryReserves","type":"uint256"}],"internalType":"struct Structs.CollateralReserves","name":"c_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"geometricMean_","type":"uint256"},{"internalType":"uint256","name":"upperRange_","type":"uint256"},{"internalType":"uint256","name":"lowerRange_","type":"uint256"},{"internalType":"uint256","name":"token0BorrowExchangePrice_","type":"uint256"},{"internalType":"uint256","name":"token1BorrowExchangePrice_","type":"uint256"}],"name":"getDebtReserves","outputs":[{"components":[{"internalType":"uint256","name":"token0Debt","type":"uint256"},{"internalType":"uint256","name":"token1Debt","type":"uint256"},{"internalType":"uint256","name":"token0RealReserves","type":"uint256"},{"internalType":"uint256","name":"token1RealReserves","type":"uint256"},{"internalType":"uint256","name":"token0ImaginaryReserves","type":"uint256"},{"internalType":"uint256","name":"token1ImaginaryReserves","type":"uint256"}],"internalType":"struct Structs.DebtReserves","name":"d_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPricesAndExchangePrices","outputs":[],"stateMutability":"nonpayable","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":"secondsAgos_","type":"uint256[]"}],"name":"oraclePrice","outputs":[{"components":[{"internalType":"uint256","name":"twap1by0","type":"uint256"},{"internalType":"uint256","name":"lowestPrice1by0","type":"uint256"},{"internalType":"uint256","name":"highestPrice1by0","type":"uint256"},{"internalType":"uint256","name":"twap0by1","type":"uint256"},{"internalType":"uint256","name":"lowestPrice0by1","type":"uint256"},{"internalType":"uint256","name":"highestPrice0by1","type":"uint256"}],"internalType":"struct Structs.Oracle[]","name":"twaps_","type":"tuple[]"},{"internalType":"uint256","name":"currentPrice_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"minSharesAmt_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"payback","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"maxToken0Payback_","type":"uint256"},{"internalType":"uint256","name":"maxToken1Payback_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"paybackPerfect","outputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"maxToken0_","type":"uint256"},{"internalType":"uint256","name":"maxToken1_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"paybackPerfectInOneToken","outputs":[{"internalType":"uint256","name":"paybackAmt_","type":"uint256"}],"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":"bool","name":"swap0to1_","type":"bool"},{"internalType":"uint256","name":"amountIn_","type":"uint256"},{"internalType":"uint256","name":"amountOutMin_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"swapIn","outputs":[{"internalType":"uint256","name":"amountOut_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"swap0to1_","type":"bool"},{"internalType":"uint256","name":"amountIn_","type":"uint256"},{"internalType":"uint256","name":"amountOutMin_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"swapInWithCallback","outputs":[{"internalType":"uint256","name":"amountOut_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"swap0to1_","type":"bool"},{"internalType":"uint256","name":"amountOut_","type":"uint256"},{"internalType":"uint256","name":"amountInMax_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"swapOut","outputs":[{"internalType":"uint256","name":"amountIn_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"swap0to1_","type":"bool"},{"internalType":"uint256","name":"amountOut_","type":"uint256"},{"internalType":"uint256","name":"amountInMax_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"swapOutWithCallback","outputs":[{"internalType":"uint256","name":"amountIn_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"maxSharesAmt_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"minToken0Withdraw_","type":"uint256"},{"internalType":"uint256","name":"minToken1Withdraw_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"withdrawPerfect","outputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"minToken0_","type":"uint256"},{"internalType":"uint256","name":"minToken1_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"withdrawPerfectInOneToken","outputs":[{"internalType":"uint256","name":"withdrawAmt_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6103606040523480156200001257600080fd5b5060405162005f4938038062005f49833981016040819052620000359162000518565b3060e052805160805260208101516001600160a01b0390811661018052604082015181166101a05260a08083015182169081905260c080840151909216918290528291829110620000a2576040516317f71f0760e11b815261c76160048201526024015b60405180910390fd5b60a051620000b09062000313565b610120526101005260c051620000c69062000313565b61016052610140526060810151516001600160a01b031615620000fc576060810151516001600160a01b03166101c05262000102565b306101c0525b6060810151602001516001600160a01b03161562000136576060810151602001516001600160a01b03166101e0526200013c565b306101e0525b6060810151604001516001600160a01b03161562000170576060810151604001516001600160a01b03166102005262000176565b30610200525b60608082015101516001600160a01b031615620001a85760608082015101516001600160a01b031661022052620001ae565b30610220525b6060810151608001516001600160a01b031615620001e2576060810151608001516001600160a01b031661024052620001e8565b30610240525b60808101516001600160a01b03166102605260e0810151610280526101008101516102a0526101208101516102c0526101408101516102e05261016081015161030052610180810151610320526101a081015161ffff101562000263576040516317f71f0760e11b815261c762600482015260240162000099565b6101a0015161034052506060810151516001600160a01b031615806200029857506060810151602001516001600160a01b0316155b80620002b357506060810151604001516001600160a01b0316155b80620002cd575060608082015101516001600160a01b0316155b80620002e857506060810151608001516001600160a01b0316155b156200030c576040516317f71f0760e11b815261c772600482015260240162000099565b5062000771565b6000808062000322846200037b565b9050600c8111156200035457600192506200033f600c8262000624565b6200034c90600a62000737565b915062000375565b6200036181600c62000624565b6200036e90600a62000737565b9250600191505b50915091565b60006001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146200041057816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200040791906200074c565b60ff1662000413565b60125b92915050565b6040516101c081016001600160401b03811182821017156200044b57634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200046957600080fd5b919050565b600060a082840312156200048157600080fd5b60405160a081016001600160401b0381118282101715620004b257634e487b7160e01b600052604160045260246000fd5b604052905080620004c38362000451565b8152620004d36020840162000451565b6020820152620004e66040840162000451565b6040820152620004f96060840162000451565b60608201526200050c6080840162000451565b60808201525092915050565b600061024082840312156200052c57600080fd5b6200053662000419565b82518152620005486020840162000451565b60208201526200055b6040840162000451565b60408201526200056f84606085016200046e565b60608201526101006200058481850162000451565b60808301526101206200059981860162000451565b60a0840152610140620005ae81870162000451565b60c08501526101608087015160e086015261018080880151858701526101a0945084880151848701526101c0880151838701526101e088015182870152610200880151818701525050505061022084015181830152508091505092915050565b634e487b7160e01b600052601160045260246000fd5b818103818111156200041357620004136200060e565b600181815b808511156200067b5781600019048211156200065f576200065f6200060e565b808516156200066d57918102915b93841c93908002906200063f565b509250929050565b600082620006945750600162000413565b81620006a35750600062000413565b8160018114620006bc5760028114620006c757620006e7565b600191505062000413565b60ff841115620006db57620006db6200060e565b50506001821b62000413565b5060208310610133831016604e8410600b84101617156200070c575081810a62000413565b6200071883836200063a565b80600019048211156200072f576200072f6200060e565b029392505050565b600062000745838362000683565b9392505050565b6000602082840312156200075f57600080fd5b815160ff811681146200074557600080fd5b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e05161030051610320516103405161551062000a3960003960008181610ff0015281816112690152818161131a01528181613dba01528181613e230152613ed0015260008181610fc9015281816129c5015261311f015260008181610fa20152818161298e015261303d015260008181610f7b0152611936015260008181610f540152612ae4015260008181610f2d0152611906015260008181610f070152612ab4015260008181610edf01528181612cdc0152613823015260008181610856015281816108ba0152610eb60152600081816107970152610e8f01526000818161093d0152610e640152600081816105f50152610e39015260008181610e12015281816145330152818161463301526146c4015260008181610429015281816104e30152610d8f015260008181610a9401528181610c5e01528181610d63015281816127c2015281816128650152818161306701528181613149015281816132b50152613a2401526000818161076901528181611c5f0152818161249a015281816124ee0152818161254a015281816125a1015261335f01526000818161074301528181611c8001528181612479015281816124cd015281816125260152818161257d015261338001526000818161071d01528181611ba2015281816123ef01528181612446015281816125f50152818161264901526133b60152600081816106fa01528181611bc3015281816123cb01528181612422015281816125d40152818161262801526133d701526000505060008181610de301528181611b5601528181611c3b01526138bc015260008181610dbb01528181611b7e01528181611c1301526138940152600081816103d90152610d2b01526155106000f3fe6080604052600436106101845760003560e01c80636560abaa116100d6578063be17c79c1161007f578063e27203cd11610059578063e27203cd1461027f578063e980e1eb146103b9578063f4b9a3fb146103c7576101bd565b8063be17c79c14610378578063d331bef7146102b4578063d811b2ce1461038b576101bd565b8063ad207501116100b0578063ad20750114610317578063b5c736e414610337578063b7791bf214610356576101bd565b80636560abaa146102e25780636876698114610271578063916cef4e14610302576101bd565b806330acd6fd116101385780634d9036de116101125780634d9036de146102d45780635b3d38d7146102d4578063653295aa1461025e576101bd565b806330acd6fd1461027157806335f0df981461027f5780634c89bfd4146102b4576101bd565b8063242011d511610169578063242011d51461021d5780632668dfaa1461024b578063286f0e611461025e576101bd565b806305d455a9146101c55780631595cbd3146101fb576101bd565b366101bd577fffffffff0000000000000000000000000000000000000000000000000000000060003516156101bb576101bb6103fb565b005b6101bb6103fb565b3480156101d157600080fd5b506101e56101e0366004614b99565b61067d565b6040516101f29190614bd4565b60405180910390f35b34801561020757600080fd5b506102106106cd565b6040516101f29190614c17565b34801561022957600080fd5b5061023d610238366004614c67565b610790565b6040519081526020016101f2565b61023d610259366004614cb6565b61080f565b61023d61026c366004614cb6565b61084f565b61023d610238366004614cf5565b34801561028b57600080fd5b5061029f61029a366004614c67565b6108b2565b604080519283526020830191909152016101f2565b3480156102c057600080fd5b5061023d6102cf366004614c67565b610936565b61029f61029a366004614cf5565b3480156102ee57600080fd5b506102106102fd366004614b99565b610999565b34801561030e57600080fd5b506101bb6109d1565b34801561032357600080fd5b506101bb610332366004614d2b565b610a7c565b34801561034357600080fd5b5061023d610352366004614db4565b5490565b34801561036257600080fd5b5061036b610c8c565b6040516101f29190614dcd565b61023d610386366004614cb6565b611018565b34801561039757600080fd5b506103ab6103a6366004614f8e565b611058565b6040516101f292919061505e565b61023d6102cf366004614cf5565b3480156103d357600080fd5b5061023d7f000000000000000000000000000000000000000000000000000000000000000081565b6040517f4502d0630000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690634502d06390602401602060405180830381865afa158015610485573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104a991906150e5565b8061056357506040517ffbeeca2c0000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063fbeeca2c90604401602060405180830381865afa15801561053f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056391906150e5565b6105a2576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73a60048201526024015b60405180910390fd5b600054600180821690036105e6576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7396004820152602401610599565b806001176000819055506106517f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b5050600080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe169055565b6106b66040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6106c386868686866118c6565b9695505050505050565b6106f86040518060800160405280600081526020016000815260200160008152602001600081525090565b7f000000000000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060208201527f000000000000000000000000000000000000000000000000000000000000000060408201527f0000000000000000000000000000000000000000000000000000000000000000606082015290565b60006107f37f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b8060200190518101906108069190615102565b95945050505050565b6000610806858560405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200187815260200160001515815250611a0e565b60006107f37f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b6000806109167f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b806020019051810190610929919061511b565b9150915094509492505050565b60006107f37f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b6109c46040518060800160405280600081526020016000815260200160008152602001600081525090565b6106c38686868686612a82565b6000546001546109e18282612bbd565b60006109f1600054600154612c48565b604080517f23bfdd3b0000000000000000000000000000000000000000000000000000000081528251600482015260208301516024820152908201516044820152606082015160648201526080820151608482015260a082015160a482015260c082015160c482015260e082015160e482015261010082015161010482015290915061012401610599565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610aef576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c75d6004820152602401610599565b600054600116600003610b32576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c75e6004820152602401610599565b60608114610b70576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7746004820152602401610599565b60008080610b808486018661513f565b92509250925085831015610bc4576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7756004820152602401610599565b8115610c57576040517f9410ae8800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015260248201859052821690639410ae8890604401600060405180830381600087803b158015610c3a57600080fd5b505af1158015610c4e573d6000803e3d6000fd5b50505050610c83565b610c8387827f0000000000000000000000000000000000000000000000000000000000000000866131a5565b50505050505050565b610d29604080516101c08101825260008082526020808301829052828401829052835160a08101855282815290810182905292830181905260608381018290526080840191909152909190820190815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e08201819052610100820181905261012082018190526101409091015290565b7f0000000000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166020808401919091527f000000000000000000000000000000000000000000000000000000000000000082166040808501919091527f0000000000000000000000000000000000000000000000000000000000000000831660a08501527f0000000000000000000000000000000000000000000000000000000000000000831660c0850152606080850180517f00000000000000000000000000000000000000000000000000000000000000008616905280517f0000000000000000000000000000000000000000000000000000000000000000861694019390935282517f0000000000000000000000000000000000000000000000000000000000000000851692019190915281517f00000000000000000000000000000000000000000000000000000000000000008416910152517f000000000000000000000000000000000000000000000000000000000000000082166080918201527f0000000000000000000000000000000000000000000000000000000000000000909116908201527f000000000000000000000000000000000000000000000000000000000000000060e08201527f00000000000000000000000000000000000000000000000000000000000000006101008201527f00000000000000000000000000000000000000000000000000000000000000006101208201527f00000000000000000000000000000000000000000000000000000000000000006101408201527f00000000000000000000000000000000000000000000000000000000000000006101608201527f00000000000000000000000000000000000000000000000000000000000000006101808201527f00000000000000000000000000000000000000000000000000000000000000006101a082015290565b6000610806858560405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200187815260200160011515815250611a0e565b6060600061108e6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6000805490600160c383901c1690036110d7576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c77a6004820152602401610599565b845167ffffffffffffffff8111156110f1576110f1614f5f565b60405190808252806020026020018201604052801561115b57816020015b6111486040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b81526020019060019003908161110f5790505b5093506000806000808860008151811061117757611177615181565b602090810291909101810151603187901c63ffffffff16602988901c60ff161b808952918801829052600760b088901c811660408a0181905261ffff60b38a901c1660608b0152929950909250889160009182918291116111d95760006111ee565b60608a01516000908152600660205260409020545b60808b01526000804260798c901c6401ffffffff1610156112255761121e60798c901c6401ffffffff16426151df565b9850611231565b61122e836151f2565b92505b8260020361146e578b6040018051611248906151f2565b908190526008036112bc57600060408d0181905260608d0151900361128e577f000000000000000000000000000000000000000000000000000000000000000060608d01525b600660008d60600180516112a19061522a565b90819052815260208101919091526040016000205460808d01525b63ffffffff8c6040015160206112d2919061525f565b60808e0151901c1691508115611438576101ff82169850886000036113ad57600982901c98508b6040015160070361137b57600060408d0181905260608d0151900361133f577f000000000000000000000000000000000000000000000000000000000000000060608d01525b600660008d60600180516113529061522a565b90819052815260208101919091526040016000205460808d0181905263ffffffff1691506113ad565b8b604001805161138a906151f2565b905260408c015163ffffffff906113a290602061525f565b8d60800151901c1691505b50600a81901c623fffff6113c88266b1a2bc2ec5000061525f565b6113d291906152a5565b9050600982901c60011660010361141157670de0b6b3a76400006113f6828861525f565b61140091906152a5565b61140a90876151df565b95506114bc565b670de0b6b3a7640000611424828861525f565b61142e91906152a5565b61140a90876152b9565b6040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76a6004820152602401610599565b826001036114a857609a8b901c623fffff16985063ffffffff60098c901c1660ff60018d901c161b95506114a1836151f2565b92506114bc565b826000036114bc576114b9836151f2565b92505b6114c6898b6152b9565b9950858c6000015111156114d857858c525b858c6020015110156114ec5760208c018690525b868a1015611547576114fe898761525f565b61150890866152b9565b94508861152c87760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b611536919061525f565b61154090856152b9565b9350611231565b89611552888b6152b9565b61155c91906151df565b9850611568898761525f565b61157290866152b9565b94508861159687760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6115a0919061525f565b6115aa90856152b9565b93506115b687866152a5565b94506115c287856152a5565b93506040518060c001604052808681526020018d6000015181526020018d6020015181526020018581526020018d60200151760a70c3c40a64e6c51999090b65f67d924000000000000061161691906152a5565b81526020018d60000151760a70c3c40a64e6c51999090b65f67d924000000000000061164291906152a5565b8152508e898151811061165757611657615181565b602090810291909101810191909152868d528c018690525b8e5161167a896151f2565b98508810156118625761168d878b6151df565b99508998508e61169e60018a6151df565b815181106116ae576116ae615181565b60200260200101518f89815181106116c8576116c8615181565b60200260200101516116da91906151df565b9650868a1015611756576116ee898761525f565b94508861171287760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b61171c919061525f565b935088600003611751577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c52600060208d01525b611862565b89611761888b6152b9565b61176b91906151df565b9850611777898761525f565b94508861179b87760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6117a5919061525f565b93506117b187866152a5565b94506117bd87856152a5565b93506040518060c001604052808681526020018d6000015181526020018d6020015181526020018581526020018d60200151760a70c3c40a64e6c51999090b65f67d924000000000000061181191906152a5565b81526020018d60000151760a70c3c40a64e6c51999090b65f67d924000000000000061183d91906152a5565b8152508e898151811061185257611852615181565b602002602001018190525061166f565b8e51880361187b57505050505050505050505050915091565b611231565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e816118be57806000803e806000fd5b505092915050565b6118ff6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600061192d7f000000000000000000000000000000000000000000000000000000000000000085600161326c565b9050600061195d7f000000000000000000000000000000000000000000000000000000000000000085600061326c565b8284526020840181905290506b033b2e3c9fd0803ce80000008810156119a2576119898887848461340f565b60a0870152608086015260608501526040840152611a03565b6119ee6119c689760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6119e789760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b838561340f565b608087015260a0860152604085015260608401525b505095945050505050565b60008054600180549060ff82901c9003611a58576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7636004820152602401610599565b611a628282612bbd565b835173ffffffffffffffffffffffffffffffffffffffff16611a82573384525b611b39604051806101600160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081526020016000815260200160001515815260200160008152602001606081525090565b8615611bfc5773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000001681527f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000870281611bf157611bf1615276565b046040820152611cb5565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000001681527f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000870281611cae57611cae615276565b0460408201525b611cc3816040015187613621565b6000611ccf8484612c48565b90503415611d7c57863414611d14576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73f6004820152602401610599565b815173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611d7c576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7406004820152602401610599565b6201ffff600284901c16607f601385901c1681026305f5e1000360e0840152620f424081900360c0840152604080516080810182526000808252602082018190529181018290526060810182905260018087169387821c909116929091611e126040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b85600103611e97576000611e3d886080015189604001518a606001518b60a001518c60e00151612a82565b90508e15611e6c5780516020808301516040808501516060808701519089015290870152908501528352611e95565b602080820151825160608085015160408087015192890192909252908701529185019190915283525b505b84600103611f48576000611ec3886080015189604001518a606001518b60c001518c61010001516118c6565b90508e15611f0657805160208083015160408085015160608087015160808089015160a0808b0151908c0152908a01529088015290860152908401528252611f46565b602080820151825160608085015160408087015160a0808901516080808b0151928c0192909252908a015292880192909252908601529184019190915282525b505b6002816080015183604001510181611f6257611f62615276565b0488604001511115611fa4576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7656004820152602401610599565b856001148015611fb45750846001145b15611fe157611fda8860400151836060015184604001518460a0015185608001516136a1565b6101208901525b8761012001518860400151138015611ffe57506000886101200151135b156120495761012088015160408901518d5173ffffffffffffffffffffffffffffffffffffffff90811660608c01528e511660808b0152909650600095508690039350849250612130565b856001148015612057575084155b8061206b5750876040015188610120015112155b156120a35760408801518c5173ffffffffffffffffffffffffffffffffffffffff1660608a0152955060009450849350839250612130565b851580156120b15750846001145b806120c25750600088610120015113155b156120fa5760408801518c5173ffffffffffffffffffffffffffffffffffffffff1660808a0152600096508695509350849250612130565b6040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7416004820152602401610599565b85156121cd57612166620f42408960c001518861214d919061525f565b61215791906152a5565b83604001518460600151613728565b94508d61219f5761219a85836020015161218091906151df565b835161218d9089906152b9565b896020015161271061374a565b6121cd565b81516121cd906121b09088906152b9565b8684602001516121c091906151df565b89602001516127106137b7565b831561225557612203620f42408960c00151866121ea919061525f565b6121f491906152a5565b82608001518360a00151613728565b92508d6122325761222d83826060015161221d91906151df565b85836040015161218d91906152b9565b612255565b61225584826040015161224591906152b9565b8483606001516121c091906151df565b6305f5e1008860e001518761226a919061525f565b61227491906152a5565b95506305f5e1008860e001518561228b919061525f565b61229591906152a5565b935083861115612333578d6122e9578482606001516122b491906151df565b8683604001516122c491906152b9565b6122da906b033b2e3c9fd0803ce800000061525f565b6122e491906152a5565b612329565b8582604001516122f991906152b9565b85836060015161230991906151df565b61231f906b033b2e3c9fd0803ce800000061525f565b61232991906152a5565b60a08901526123c3565b8d61237d57828160a0015161234891906151df565b84826080015161235891906152b9565b61236e906b033b2e3c9fd0803ce800000061525f565b61237891906152a5565b6123bd565b83816080015161238d91906152b9565b838260a0015161239d91906151df565b6123b3906b033b2e3c9fd0803ce800000061525f565b6123bd91906152a5565b60a08901525b8d15612524577f00000000000000000000000000000000000000000000000000000000000000006124147f00000000000000000000000000000000000000000000000000000000000000008861525f565b61241e91906152a5565b95507f000000000000000000000000000000000000000000000000000000000000000061246b7f00000000000000000000000000000000000000000000000000000000000000008661525f565b61247591906152a5565b93507f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008602816124c8576124c8615276565b0494507f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000084028161251c5761251c615276565b04925061267b565b7f000000000000000000000000000000000000000000000000000000000000000061256f7f00000000000000000000000000000000000000000000000000000000000000008861525f565b61257991906152a5565b95507f00000000000000000000000000000000000000000000000000000000000000006125c67f00000000000000000000000000000000000000000000000000000000000000008661525f565b6125d091906152a5565b93507f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000086028161262357612623615276565b0494507f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000084028161267757612677615276565b0492505b8b518386019b5073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2153016126f3576040517fb3bfda99000000000000000000000000000000000000000000000000000000008152600481018c9052602401610599565b8b602001518b1015612735576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7426004820152602401610599565b8c8c604001513360405160200161277593929190928352901515602083015273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052610140890152875173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063ad967e15903490896127f5896152cc565b6000808f61014001516040518863ffffffff1660e01b815260040161281f96959493929190615304565b604080518083038185885af115801561283c573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612861919061511b565b50507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad967e158960200151876128ae906152cc565b60608c015160808d0151604080516000815260208101918290527fffffffff0000000000000000000000000000000000000000000000000000000060e088901b169091526129059493928a92909160248101615304565b60408051808303816000875af1158015612923573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612947919061511b565b5050608e89901c633fffffff169550851561297b578d1515610100890181905260a089015161297b9188916001919061381e565b8d6129b7576129b26103ff60e48b901c167f00000000000000000000000000000000000000000000000000000000000000006139c5565b6129e9565b6129e96103ff60ee8b901c167f00000000000000000000000000000000000000000000000000000000000000006139c5565b6129fc8860a0015188602001518c613adb565b6000819055507fdc004dbca4ef9c966218431ee5d9133d337ad018dd5b5c5493722803f75c64f78e8e8d8f60000151604051612a69949392919093151584526020840192909252604083015273ffffffffffffffffffffffffffffffffffffffff16606082015260800190565b60405180910390a1505050505050505050509392505050565b612aad6040518060800160405280600081526020016000815260200160008152602001600081525090565b6000612adb7f000000000000000000000000000000000000000000000000000000000000000085600161326c565b90506000612b0b7f000000000000000000000000000000000000000000000000000000000000000085600061326c565b90506b033b2e3c9fd0803ce8000000881015612b3c57612b2d88888484613fa0565b60608501526040840152612b93565b612b88612b6089760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b612b8188760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b8385613fa0565b604085015260608401525b81835260208301819052604083018051909201909152606082018051909101905295945050505050565b81600116600103612bfe576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7396004820152602401610599565b80600316600003612c3f576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73d6004820152602401610599565b50600117600055565b612c976040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600060f883901c600116600003612d7857633fffffff607084901c16905080600003612cd7575063ffffffff605984901c1660ff605185901c161b612d85565b612d017f0000000000000000000000000000000000000000000000000000000000000000826140ad565b73ffffffffffffffffffffffffffffffffffffffff1663f763f7a66040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d719190615102565b9050612d85565b612d828484614519565b90505b63ffffffff603185901c1660ff602986901c161b601b84901c620fffff90811690602f86901c166001601a87901c81169003612dce57612dc682828861460e565b975090925090505b81620f424003620f4240850281612de757612de7615276565b049150620f42409081038402046000620fffff604488901c1615612eed57604487901c6103ff90811690604e89901c16605889901c62ffffff16600160438b901c81169003612e4357612e3b83838361469f565b919450925090505b6103e888870384820302048801871115612e985760798b901c6401ffffffff16420381811015612e8a5781818a89030281612e8057612e80615276565b0489019850612e8e565b8698505b6001945050612ee9565b6103e885890383820302048803871015612ee95760798b901c6401ffffffff16420381811015612edf578181878b030281612ed557612ed5615276565b0489039850612ee3565b8598505b60019450505b5050505b620fffff60b488901c1660ff60ac89901c161b80861115612f145780955060019150612f38565b50620fffff60d088901c1660ff60c889901c161b80861015612f3857809550600191505b8115612fa157620fffff601b89901c169350620fffff602f89901c169250601a88901c600116600103612f7857612f7084848a61460e565b995090945092505b83620f424003620f4240870281612f9157612f91615276565b049350620f424083810387020492505b8487526020870186905260408701849052606087018390526f4b3b4ca85a86c47a098a224000000000841015612fe657612fdc83850261470e565b6080880152613015565b613005670de0b6b3a76400008404670de0b6b3a764000086040261470e565b670de0b6b3a76400000260808801525b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201526130ed907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024015b602060405180830381865afa1580156130c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130e89190615102565b6147c0565b60c089015260a08801526040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f0000000000000000000000000000000000000000000000000000000000000000600482015261318d907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024016130a7565b61010089015260e08801525094979650505050505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080613265576040517fdee51a8a000000000000000000000000000000000000000000000000000000008152620115596004820152602401610599565b5050505050565b6040517fb5c736e400000000000000000000000000000000000000000000000000000000815260048101849052600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b5c736e490602401602060405180830381865afa1580156132fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133209190615102565b905067ffffffffffffffff600182901c16915060ff8216600883901c901b9150806001166001036133585764e8d4a510008285020491505b826133b4577f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008302816133ae576133ae615276565b04610806565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083028161340557613405615276565b0495945050505050565b6000808080806b06765c793fa10079d0000000613438876b033b2e3c9fd0803ce800000061525f565b6134428b8a61525f565b61344c91906153b1565b61345691906153d8565b90506000613464878961525f565b905074446c3b15f9926687d2c40534fdb56400000000000081106134a857886134996b033b2e3c9fd0803ce8000000836152a5565b6134a3919061525f565b6134c9565b6b033b2e3c9fd0803ce80000006134bf8a8361525f565b6134c991906152a5565b90506134e76134d88380615440565b6134e290836152b9565b61470e565b6134f1908361548c565b94506134fd898961525f565b613513866b033b2e3c9fd0803ce800000061525f565b61351d91906151df565b9250620f4240831015613560576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76d6004820152602401610599565b6a084595161401484a0000008510156135a5578261357e868061525f565b613594906b033b2e3c9fd0803ce800000061525f565b61359e91906152a5565b92506135d2565b6135bb6b033b2e3c9fd0803ce8000000846152a5565b6135c5868061525f565b6135cf91906152a5565b92505b87856135de828661525f565b6135e891906152a5565b6135f291906151df565b93506135fe87846152b9565b613608888661525f565b61361291906152a5565b95505050945094509450949050565b620f424082108061363d57506bffffffffffffffffffffffff82115b806136485750606481105b8061366257506fffffffffffffffffffffffffffffffff81115b1561369d576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7696004820152602401610599565b5050565b6000806136c36136b1868861525f565b6134e290670de0b6b3a764000061525f565b905060006136d46136b1858761525f565b90506136e081836152b9565b6136ea828861525f565b6136f4848b61525f565b6136fe858861525f565b61370891906152b9565b61371291906153b1565b61371c91906153d8565b98975050505050505050565b600083820283850180828161373f5761373f615276565b049695505050505050565b613754818361525f565b61376a846b033b2e3c9fd0803ce800000061525f565b61377491906152a5565b8410156137b1576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73e6004820152602401610599565b50505050565b6137cd816b033b2e3c9fd0803ce800000061525f565b6137d7838661525f565b6137e191906152a5565b8310156137b1576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73e6004820152602401610599565b6138487f0000000000000000000000000000000000000000000000000000000000000000856140ad565b6040517fd1f9be4e00000000000000000000000000000000000000000000000000000000815260048101859052831515602482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660448301527f00000000000000000000000000000000000000000000000000000000000000008116606483015260848201849052919091169063d1f9be4e9060a4016020604051808303816000875af1925050508015613950575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261394d918101906150e5565b60015b61398a573d80801561397e576040519150601f19603f3d011682016040523d82523d6000602084013e613983565b606091505b50506137b1565b80613265576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7446004820152602401610599565b6103e882101561369d576139da82600a61525f565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081526004810183905290925060009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b5c736e490602401602060405180830381865afa158015613a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a8f9190615102565b601e1c613fff16905082811115613ad6576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7436004820152602401610599565b505050565b600080613af3607984901c6401ffffffff16426151df565b9050600080600083600003613bdb5763ffffffff605987901c1660ff605188901c161b92506305f5e1006305f5e0ff840204871080613b3d57506305f5e1006305f5e10184020487115b15613b78576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76c6004820152602401610599565b63ffffffff600987901c1660ff600188901c161b9250613b9883896149ce565b506029613baa89602060086000614a70565b901b867ffffffffffffffffffffffffffffffffffffffffffffe0000000001ffffffffff1617945050505050613f99565b60c386901c600116600003613c755763ffffffff603187901c1660ff602988901c161b9250613c0a83896149ce565b504260791b6051613c2089602060086000614a70565b901b6029613c338b602060086000614a70565b7ffffffffffffffffffffffffffc000000000000000000000000000000000000018a166501fffffffffe60288c901c1617911b1717179450613f999350505050565b63ffffffff600987901c811660ff600189901c81169190911b9450603188901c909116602988901c9091161b91506000613caf838a6149ce565b905082670de0b6b3a7640000850281613cca57613cca615276565b04670de0b6b3a76400000390506000811215613d0a57600166b1a2bc2ec50000623fffff836000030281613d0057613d00615276565b04901b9150613d23565b600166b1a2bc2ec50000623fffff830204901b60011791505b623fffff851115613d3557623fffff94505b609a87901c623fffff16935060b087901c60071660b388901c61ffff166101ff861115613e70578115613e015760299390931b600986901b1792613d788261522a565b9150613d8582602061525f565b600082815260066020526040902080549186901b90911790558115613db457613dad8261522a565b9150613f13565b600791507f00000000000000000000000000000000000000000000000000000000000000008160010181613dea57613dea615276565b066000818152600660205260408120559050613f13565b6000818152600660208190526040909120805460099690961b958617905591507f00000000000000000000000000000000000000000000000000000000000000006001820181613e5357613e53615276565b06600081815260066020526040902060e988901b90559050613f13565b85600985901b1793506007821015613ea5576000818152600660209081526040909120805491840286901b9091179055613ebb565b600081815260066020526040902060e085901b90555b8115613eca57613dad8261522a565b600791507f00000000000000000000000000000000000000000000000000000000000000008160010181613f0057613f00615276565b0660008181526006602052604081205590505b8a955089945088935060b381901b60b083901b609a89901b4260791b6051613f408a602060086000614a70565b901b6029613f538c602060086000614a70565b901b600164ffffffffff60298c901c16901b8a7ffffffffffffffff800000000000000000000000000000000000000000000000116171717171717179750505050505050505b9392505050565b60008080613fae87876151df565b90506000613fbd82600261525f565b613fd3866b033b2e3c9fd0803ce800000061525f565b613fdd888b61525f565b613fe791906152b9565b613ff191906152a5565b90506000613fff868861525f565b905074446c3b15f9926687d2c40534fdb56400000000000081106140425761402783826152a5565b61403d906b033b2e3c9fd0803ce800000061525f565b614063565b82614059826b033b2e3c9fd0803ce800000061525f565b61406391906152a5565b90506140726134d8838061525f565b61407c90836152b9565b94506b033b2e3c9fd0803ce80000006140958a8761525f565b61409f91906152a5565b935050505094509492505050565b60006060826000036140c3576000915050614513565b607f831161418c576040517fd60000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16602282015260f884901b7fff000000000000000000000000000000000000000000000000000000000000001660368201526037015b6040516020818303038152906040529050614508565b60ff8311614269576040517fd70000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f8100000000000000000000000000000000000000000000000000000000000000603682015260f884901b7fff00000000000000000000000000000000000000000000000000000000000000166037820152603801614176565b61ffff8311614347576040517fd80000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f820000000000000000000000000000000000000000000000000000000000000060368201527fffff00000000000000000000000000000000000000000000000000000000000060f085901b166037820152603901614176565b62ffffff8311614426576040517fd90000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f830000000000000000000000000000000000000000000000000000000000000060368201527fffffff000000000000000000000000000000000000000000000000000000000060e885901b166037820152603a01614176565b6040517fda0000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f840000000000000000000000000000000000000000000000000000000000000060368201527fffffffff0000000000000000000000000000000000000000000000000000000060e085901b166037820152603b0160405160208183030381529060405290505b805160209091012090505b92915050565b60405160248101839052604481018290526000906145fb907f0000000000000000000000000000000000000000000000000000000000000000907f2f850fb300000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611880565b806020019051810190613f999190615102565b6040516024810184905260448101839052606481018290526000908190819061467d907f0000000000000000000000000000000000000000000000000000000000000000907f0d7cf0ab0000000000000000000000000000000000000000000000000000000090608401614579565b80602001905181019061469091906154ac565b92509250925093509350939050565b6040516024810184905260448101839052606481018290526000908190819061467d907f0000000000000000000000000000000000000000000000000000000000000000907fe8518a660000000000000000000000000000000000000000000000000000000090608401614579565b60b5817101000000000000000000000000000000000081106147355760409190911b9060801c5b690100000000000000000081106147515760209190911b9060401c5b6501000000000081106147695760109190911b9060201c5b6301000000811061477f5760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b67ffffffffffffffff605b82901c811690609b83901c168115806147e2575080155b1561481e576040517fd50d7512000000000000000000000000000000000000000000000000000000008152620111716004820152602401610599565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480614848575082155b806148535750806001145b1561486057505050915091565b64496cebb80084840283020484019350617fff60db87901c1692508260010361488b57505050915091565b826001166001036148e05760019290921c91826c7e37be2022c0914b2680000000816148b9576148b9615276565b049250612710601e87901c613fff166b033b2e3c9fd0803ce800000085010204925061490d565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b806001166001036149445760011c61271081016b033b2e3c9fd0803ce800000082028161493c5761493c615276565b04905061497a565b60011c61271081016b033b2e3c9fd0803ce800000082028161496857614968615276565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b6000816149e3670de0b6b3a76400008561525f565b6149ed91906152a5565b6149ff90670de0b6b3a76400006153b1565b905066b1a2bc2ec50000811380614a3557507fffffffffffffffffffffffffffffffffffffffffffffffffff4e5d43d13b000081125b15614513576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7606004820152602401610599565b600080856fffffffffffffffffffffffffffffffff811115614a935760809150811c5b67ffffffffffffffff811115614aab576040918201911c5b63ffffffff811115614abf576020918201911c5b61ffff811115614ad1576010918201911c5b60ff811115614ae2576008918201911c5b600f811115614af3576004918201911c5b6003811115614b04576002918201911c5b6001811115614b14576001820191505b8015614b21576001820191505b5084811015614b2d5750835b848103905085811c60008211841615614b7c57600181019050806001871b03614b7c57506001908101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86011b5b6001851b8210614b8b57600080fd5b90931b909201949350505050565b600080600080600060a08688031215614bb157600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b60c081016145138284805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b8151815260208083015190820152604080830151908201526060808301519082015260808101614513565b73ffffffffffffffffffffffffffffffffffffffff81168114614c6457600080fd5b50565b60008060008060808587031215614c7d57600080fd5b8435935060208501359250604085013591506060850135614c9d81614c42565b939692955090935050565b8015158114614c6457600080fd5b60008060008060808587031215614ccc57600080fd5b8435614cd781614ca8565b935060208501359250604085013591506060850135614c9d81614c42565b60008060008060808587031215614d0b57600080fd5b8435935060208501359250604085013591506060850135614c9d81614ca8565b60008060008060608587031215614d4157600080fd5b8435614d4c81614c42565b935060208501359250604085013567ffffffffffffffff80821115614d7057600080fd5b818701915087601f830112614d8457600080fd5b813581811115614d9357600080fd5b886020828501011115614da557600080fd5b95989497505060200194505050565b600060208284031215614dc657600080fd5b5035919050565b81518152602080830151610240830191614dfe9084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151614e26604084018273ffffffffffffffffffffffffffffffffffffffff169052565b506060830151614e84606084018273ffffffffffffffffffffffffffffffffffffffff808251168352806020830151166020840152806040830151166040840152806060830151166060840152806080830151166080840152505050565b506080830151610100614eae8185018373ffffffffffffffffffffffffffffffffffffffff169052565b60a08501519150610120614ed98186018473ffffffffffffffffffffffffffffffffffffffff169052565b60c08601519250610140614f048187018573ffffffffffffffffffffffffffffffffffffffff169052565b60e0870151610160878101919091529287015161018080880191909152918701516101a080880191909152908701516101c0870152918601516101e08601528501516102008501529093015161022090920191909152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020808385031215614fa157600080fd5b823567ffffffffffffffff80821115614fb957600080fd5b818501915085601f830112614fcd57600080fd5b813581811115614fdf57614fdf614f5f565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110858211171561502257615022614f5f565b60405291825284820192508381018501918883111561504057600080fd5b938501935b8285101561371c57843584529385019392850192615045565b604080825283519082018190526000906020906060840190828701845b828110156150d6576150c3848351805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b60c093909301929084019060010161507b565b50505092019290925292915050565b6000602082840312156150f757600080fd5b8151613f9981614ca8565b60006020828403121561511457600080fd5b5051919050565b6000806040838503121561512e57600080fd5b505080516020909101519092909150565b60008060006060848603121561515457600080fd5b83359250602084013561516681614ca8565b9150604084013561517681614c42565b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115614513576145136151b0565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615223576152236151b0565b5060010190565b600081615239576152396151b0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b8082028115828204841417614513576145136151b0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826152b4576152b4615276565b500490565b80820180821115614513576145136151b0565b60007f800000000000000000000000000000000000000000000000000000000000000082036152fd576152fd6151b0565b5060000390565b600073ffffffffffffffffffffffffffffffffffffffff8089168352602088818501528760408501528187166060850152818616608085015260c060a0850152845191508160c085015260005b8281101561536d5785810182015185820160e001528101615351565b5050600060e0828501015260e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116840101915050979650505050505050565b81810360008312801583831316838312821617156153d1576153d16151b0565b5092915050565b6000826153e7576153e7615276565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561543b5761543b6151b0565b500590565b808202600082127f800000000000000000000000000000000000000000000000000000000000000084141615615478576154786151b0565b8181058314821517614513576145136151b0565b80820182811260008312801582168215821617156118be576118be6151b0565b6000806000606084860312156154c157600080fd5b835192506020840151915060408401519050925092509256fea2646970667358221220030a7fecc110b38b34765d7248652e1fcb76dfaaf261eed5e38b5d920a127a9a64736f6c63430008150033000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49700000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c190850000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d80000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d30000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df255430000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a727fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d39853c6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf0806962814311ec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202aa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d70000000000000000000000000000000000000000000000000000000000000400
Deployed Bytecode
0x6080604052600436106101845760003560e01c80636560abaa116100d6578063be17c79c1161007f578063e27203cd11610059578063e27203cd1461027f578063e980e1eb146103b9578063f4b9a3fb146103c7576101bd565b8063be17c79c14610378578063d331bef7146102b4578063d811b2ce1461038b576101bd565b8063ad207501116100b0578063ad20750114610317578063b5c736e414610337578063b7791bf214610356576101bd565b80636560abaa146102e25780636876698114610271578063916cef4e14610302576101bd565b806330acd6fd116101385780634d9036de116101125780634d9036de146102d45780635b3d38d7146102d4578063653295aa1461025e576101bd565b806330acd6fd1461027157806335f0df981461027f5780634c89bfd4146102b4576101bd565b8063242011d511610169578063242011d51461021d5780632668dfaa1461024b578063286f0e611461025e576101bd565b806305d455a9146101c55780631595cbd3146101fb576101bd565b366101bd577fffffffff0000000000000000000000000000000000000000000000000000000060003516156101bb576101bb6103fb565b005b6101bb6103fb565b3480156101d157600080fd5b506101e56101e0366004614b99565b61067d565b6040516101f29190614bd4565b60405180910390f35b34801561020757600080fd5b506102106106cd565b6040516101f29190614c17565b34801561022957600080fd5b5061023d610238366004614c67565b610790565b6040519081526020016101f2565b61023d610259366004614cb6565b61080f565b61023d61026c366004614cb6565b61084f565b61023d610238366004614cf5565b34801561028b57600080fd5b5061029f61029a366004614c67565b6108b2565b604080519283526020830191909152016101f2565b3480156102c057600080fd5b5061023d6102cf366004614c67565b610936565b61029f61029a366004614cf5565b3480156102ee57600080fd5b506102106102fd366004614b99565b610999565b34801561030e57600080fd5b506101bb6109d1565b34801561032357600080fd5b506101bb610332366004614d2b565b610a7c565b34801561034357600080fd5b5061023d610352366004614db4565b5490565b34801561036257600080fd5b5061036b610c8c565b6040516101f29190614dcd565b61023d610386366004614cb6565b611018565b34801561039757600080fd5b506103ab6103a6366004614f8e565b611058565b6040516101f292919061505e565b61023d6102cf366004614cf5565b3480156103d357600080fd5b5061023d7f000000000000000000000000000000000000000000000000000000000000000b81565b6040517f4502d0630000000000000000000000000000000000000000000000000000000081523360048201527f00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c1908573ffffffffffffffffffffffffffffffffffffffff1690634502d06390602401602060405180830381865afa158015610485573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104a991906150e5565b8061056357506040517ffbeeca2c0000000000000000000000000000000000000000000000000000000081523060048201523360248201527f00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c1908573ffffffffffffffffffffffffffffffffffffffff169063fbeeca2c90604401602060405180830381865afa15801561053f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056391906150e5565b6105a2576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73a60048201526024015b60405180910390fd5b600054600180821690036105e6576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7396004820152602401610599565b806001176000819055506106517f000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d86000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b5050600080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe169055565b6106b66040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6106c386868686866118c6565b9695505050505050565b6106f86040518060800160405280600081526020016000815260200160008152602001600081525090565b7f000000000000000000000000000000000000000000000000000000000000000181527f00000000000000000000000000000000000000000000000000000000000f424060208201527f000000000000000000000000000000000000000000000000000000000000000160408201527f00000000000000000000000000000000000000000000000000000000000f4240606082015290565b60006107f37f000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d36000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b8060200190518101906108069190615102565b95945050505050565b6000610806858560405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200187815260200160001515815250611a0e565b60006107f37f0000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df255436000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b6000806109167f0000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df255436000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b806020019051810190610929919061511b565b9150915094509492505050565b60006107f37f0000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b6109c46040518060800160405280600081526020016000815260200160008152602001600081525090565b6106c38686868686612a82565b6000546001546109e18282612bbd565b60006109f1600054600154612c48565b604080517f23bfdd3b0000000000000000000000000000000000000000000000000000000081528251600482015260208301516024820152908201516044820152606082015160648201526080820151608482015260a082015160a482015260c082015160c482015260e082015160e482015261010082015161010482015290915061012401610599565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e4971614610aef576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c75d6004820152602401610599565b600054600116600003610b32576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c75e6004820152602401610599565b60608114610b70576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7746004820152602401610599565b60008080610b808486018661513f565b92509250925085831015610bc4576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7756004820152602401610599565b8115610c57576040517f9410ae8800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015260248201859052821690639410ae8890604401600060405180830381600087803b158015610c3a57600080fd5b505af1158015610c4e573d6000803e3d6000fd5b50505050610c83565b610c8387827f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497866131a5565b50505050505050565b610d29604080516101c08101825260008082526020808301829052828401829052835160a08101855282815290810182905292830181905260608381018290526080840191909152909190820190815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e08201819052610100820181905261012082018190526101409091015290565b7f000000000000000000000000000000000000000000000000000000000000000b815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49781166020808401919091527f00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c1908582166040808501919091527f0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb831660a08501527f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831660c0850152606080850180517f0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b8616905280517f000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d8861694019390935282517f0000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c851692019190915281517f000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d38416910152517f0000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df2554382166080918201527f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be909116908201527f76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a7260e08201527f7fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d398536101008201527fc6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf08069628143116101208201527fec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b6101408201527f7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202a6101608201527fa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d76101808201527f00000000000000000000000000000000000000000000000000000000000004006101a082015290565b6000610806858560405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200187815260200160011515815250611a0e565b6060600061108e6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6000805490600160c383901c1690036110d7576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c77a6004820152602401610599565b845167ffffffffffffffff8111156110f1576110f1614f5f565b60405190808252806020026020018201604052801561115b57816020015b6111486040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b81526020019060019003908161110f5790505b5093506000806000808860008151811061117757611177615181565b602090810291909101810151603187901c63ffffffff16602988901c60ff161b808952918801829052600760b088901c811660408a0181905261ffff60b38a901c1660608b0152929950909250889160009182918291116111d95760006111ee565b60608a01516000908152600660205260409020545b60808b01526000804260798c901c6401ffffffff1610156112255761121e60798c901c6401ffffffff16426151df565b9850611231565b61122e836151f2565b92505b8260020361146e578b6040018051611248906151f2565b908190526008036112bc57600060408d0181905260608d0151900361128e577f000000000000000000000000000000000000000000000000000000000000040060608d01525b600660008d60600180516112a19061522a565b90819052815260208101919091526040016000205460808d01525b63ffffffff8c6040015160206112d2919061525f565b60808e0151901c1691508115611438576101ff82169850886000036113ad57600982901c98508b6040015160070361137b57600060408d0181905260608d0151900361133f577f000000000000000000000000000000000000000000000000000000000000040060608d01525b600660008d60600180516113529061522a565b90819052815260208101919091526040016000205460808d0181905263ffffffff1691506113ad565b8b604001805161138a906151f2565b905260408c015163ffffffff906113a290602061525f565b8d60800151901c1691505b50600a81901c623fffff6113c88266b1a2bc2ec5000061525f565b6113d291906152a5565b9050600982901c60011660010361141157670de0b6b3a76400006113f6828861525f565b61140091906152a5565b61140a90876151df565b95506114bc565b670de0b6b3a7640000611424828861525f565b61142e91906152a5565b61140a90876152b9565b6040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76a6004820152602401610599565b826001036114a857609a8b901c623fffff16985063ffffffff60098c901c1660ff60018d901c161b95506114a1836151f2565b92506114bc565b826000036114bc576114b9836151f2565b92505b6114c6898b6152b9565b9950858c6000015111156114d857858c525b858c6020015110156114ec5760208c018690525b868a1015611547576114fe898761525f565b61150890866152b9565b94508861152c87760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b611536919061525f565b61154090856152b9565b9350611231565b89611552888b6152b9565b61155c91906151df565b9850611568898761525f565b61157290866152b9565b94508861159687760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6115a0919061525f565b6115aa90856152b9565b93506115b687866152a5565b94506115c287856152a5565b93506040518060c001604052808681526020018d6000015181526020018d6020015181526020018581526020018d60200151760a70c3c40a64e6c51999090b65f67d924000000000000061161691906152a5565b81526020018d60000151760a70c3c40a64e6c51999090b65f67d924000000000000061164291906152a5565b8152508e898151811061165757611657615181565b602090810291909101810191909152868d528c018690525b8e5161167a896151f2565b98508810156118625761168d878b6151df565b99508998508e61169e60018a6151df565b815181106116ae576116ae615181565b60200260200101518f89815181106116c8576116c8615181565b60200260200101516116da91906151df565b9650868a1015611756576116ee898761525f565b94508861171287760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b61171c919061525f565b935088600003611751577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c52600060208d01525b611862565b89611761888b6152b9565b61176b91906151df565b9850611777898761525f565b94508861179b87760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6117a5919061525f565b93506117b187866152a5565b94506117bd87856152a5565b93506040518060c001604052808681526020018d6000015181526020018d6020015181526020018581526020018d60200151760a70c3c40a64e6c51999090b65f67d924000000000000061181191906152a5565b81526020018d60000151760a70c3c40a64e6c51999090b65f67d924000000000000061183d91906152a5565b8152508e898151811061185257611852615181565b602002602001018190525061166f565b8e51880361187b57505050505050505050505050915091565b611231565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e816118be57806000803e806000fd5b505092915050565b6118ff6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600061192d7f7fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d3985385600161326c565b9050600061195d7fec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b85600061326c565b8284526020840181905290506b033b2e3c9fd0803ce80000008810156119a2576119898887848461340f565b60a0870152608086015260608501526040840152611a03565b6119ee6119c689760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6119e789760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b838561340f565b608087015260a0860152604085015260608401525b505095945050505050565b60008054600180549060ff82901c9003611a58576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7636004820152602401610599565b611a628282612bbd565b835173ffffffffffffffffffffffffffffffffffffffff16611a82573384525b611b39604051806101600160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081526020016000815260200160001515815260200160008152602001606081525090565b8615611bfc5773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee811660208301527f0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb1681527f00000000000000000000000000000000000000000000000000000000000f42407f0000000000000000000000000000000000000000000000000000000000000001870281611bf157611bf1615276565b046040820152611cb5565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb811660208301527f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1681527f00000000000000000000000000000000000000000000000000000000000f42407f0000000000000000000000000000000000000000000000000000000000000001870281611cae57611cae615276565b0460408201525b611cc3816040015187613621565b6000611ccf8484612c48565b90503415611d7c57863414611d14576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73f6004820152602401610599565b815173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611d7c576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7406004820152602401610599565b6201ffff600284901c16607f601385901c1681026305f5e1000360e0840152620f424081900360c0840152604080516080810182526000808252602082018190529181018290526060810182905260018087169387821c909116929091611e126040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b85600103611e97576000611e3d886080015189604001518a606001518b60a001518c60e00151612a82565b90508e15611e6c5780516020808301516040808501516060808701519089015290870152908501528352611e95565b602080820151825160608085015160408087015192890192909252908701529185019190915283525b505b84600103611f48576000611ec3886080015189604001518a606001518b60c001518c61010001516118c6565b90508e15611f0657805160208083015160408085015160608087015160808089015160a0808b0151908c0152908a01529088015290860152908401528252611f46565b602080820151825160608085015160408087015160a0808901516080808b0151928c0192909252908a015292880192909252908601529184019190915282525b505b6002816080015183604001510181611f6257611f62615276565b0488604001511115611fa4576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7656004820152602401610599565b856001148015611fb45750846001145b15611fe157611fda8860400151836060015184604001518460a0015185608001516136a1565b6101208901525b8761012001518860400151138015611ffe57506000886101200151135b156120495761012088015160408901518d5173ffffffffffffffffffffffffffffffffffffffff90811660608c01528e511660808b0152909650600095508690039350849250612130565b856001148015612057575084155b8061206b5750876040015188610120015112155b156120a35760408801518c5173ffffffffffffffffffffffffffffffffffffffff1660608a0152955060009450849350839250612130565b851580156120b15750846001145b806120c25750600088610120015113155b156120fa5760408801518c5173ffffffffffffffffffffffffffffffffffffffff1660808a0152600096508695509350849250612130565b6040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7416004820152602401610599565b85156121cd57612166620f42408960c001518861214d919061525f565b61215791906152a5565b83604001518460600151613728565b94508d61219f5761219a85836020015161218091906151df565b835161218d9089906152b9565b896020015161271061374a565b6121cd565b81516121cd906121b09088906152b9565b8684602001516121c091906151df565b89602001516127106137b7565b831561225557612203620f42408960c00151866121ea919061525f565b6121f491906152a5565b82608001518360a00151613728565b92508d6122325761222d83826060015161221d91906151df565b85836040015161218d91906152b9565b612255565b61225584826040015161224591906152b9565b8483606001516121c091906151df565b6305f5e1008860e001518761226a919061525f565b61227491906152a5565b95506305f5e1008860e001518561228b919061525f565b61229591906152a5565b935083861115612333578d6122e9578482606001516122b491906151df565b8683604001516122c491906152b9565b6122da906b033b2e3c9fd0803ce800000061525f565b6122e491906152a5565b612329565b8582604001516122f991906152b9565b85836060015161230991906151df565b61231f906b033b2e3c9fd0803ce800000061525f565b61232991906152a5565b60a08901526123c3565b8d61237d57828160a0015161234891906151df565b84826080015161235891906152b9565b61236e906b033b2e3c9fd0803ce800000061525f565b61237891906152a5565b6123bd565b83816080015161238d91906152b9565b838260a0015161239d91906151df565b6123b3906b033b2e3c9fd0803ce800000061525f565b6123bd91906152a5565b60a08901525b8d15612524577f00000000000000000000000000000000000000000000000000000000000000016124147f00000000000000000000000000000000000000000000000000000000000f42408861525f565b61241e91906152a5565b95507f000000000000000000000000000000000000000000000000000000000000000161246b7f00000000000000000000000000000000000000000000000000000000000f42408661525f565b61247591906152a5565b93507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000f42408602816124c8576124c8615276565b0494507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000f424084028161251c5761251c615276565b04925061267b565b7f000000000000000000000000000000000000000000000000000000000000000161256f7f00000000000000000000000000000000000000000000000000000000000f42408861525f565b61257991906152a5565b95507f00000000000000000000000000000000000000000000000000000000000000016125c67f00000000000000000000000000000000000000000000000000000000000f42408661525f565b6125d091906152a5565b93507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000f424086028161262357612623615276565b0494507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000f424084028161267757612677615276565b0492505b8b518386019b5073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2153016126f3576040517fb3bfda99000000000000000000000000000000000000000000000000000000008152600481018c9052602401610599565b8b602001518b1015612735576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7426004820152602401610599565b8c8c604001513360405160200161277593929190928352901515602083015273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052610140890152875173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497169063ad967e15903490896127f5896152cc565b6000808f61014001516040518863ffffffff1660e01b815260040161281f96959493929190615304565b604080518083038185885af115801561283c573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612861919061511b565b50507f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff1663ad967e158960200151876128ae906152cc565b60608c015160808d0151604080516000815260208101918290527fffffffff0000000000000000000000000000000000000000000000000000000060e088901b169091526129059493928a92909160248101615304565b60408051808303816000875af1158015612923573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612947919061511b565b5050608e89901c633fffffff169550851561297b578d1515610100890181905260a089015161297b9188916001919061381e565b8d6129b7576129b26103ff60e48b901c167f7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202a6139c5565b6129e9565b6129e96103ff60ee8b901c167fa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d76139c5565b6129fc8860a0015188602001518c613adb565b6000819055507fdc004dbca4ef9c966218431ee5d9133d337ad018dd5b5c5493722803f75c64f78e8e8d8f60000151604051612a69949392919093151584526020840192909252604083015273ffffffffffffffffffffffffffffffffffffffff16606082015260800190565b60405180910390a1505050505050505050509392505050565b612aad6040518060800160405280600081526020016000815260200160008152602001600081525090565b6000612adb7f76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a7285600161326c565b90506000612b0b7fc6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf080696281431185600061326c565b90506b033b2e3c9fd0803ce8000000881015612b3c57612b2d88888484613fa0565b60608501526040840152612b93565b612b88612b6089760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b612b8188760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b8385613fa0565b604085015260608401525b81835260208301819052604083018051909201909152606082018051909101905295945050505050565b81600116600103612bfe576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7396004820152602401610599565b80600316600003612c3f576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73d6004820152602401610599565b50600117600055565b612c976040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600060f883901c600116600003612d7857633fffffff607084901c16905080600003612cd7575063ffffffff605984901c1660ff605185901c161b612d85565b612d017f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be826140ad565b73ffffffffffffffffffffffffffffffffffffffff1663f763f7a66040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d719190615102565b9050612d85565b612d828484614519565b90505b63ffffffff603185901c1660ff602986901c161b601b84901c620fffff90811690602f86901c166001601a87901c81169003612dce57612dc682828861460e565b975090925090505b81620f424003620f4240850281612de757612de7615276565b049150620f42409081038402046000620fffff604488901c1615612eed57604487901c6103ff90811690604e89901c16605889901c62ffffff16600160438b901c81169003612e4357612e3b83838361469f565b919450925090505b6103e888870384820302048801871115612e985760798b901c6401ffffffff16420381811015612e8a5781818a89030281612e8057612e80615276565b0489019850612e8e565b8698505b6001945050612ee9565b6103e885890383820302048803871015612ee95760798b901c6401ffffffff16420381811015612edf578181878b030281612ed557612ed5615276565b0489039850612ee3565b8598505b60019450505b5050505b620fffff60b488901c1660ff60ac89901c161b80861115612f145780955060019150612f38565b50620fffff60d088901c1660ff60c889901c161b80861015612f3857809550600191505b8115612fa157620fffff601b89901c169350620fffff602f89901c169250601a88901c600116600103612f7857612f7084848a61460e565b995090945092505b83620f424003620f4240870281612f9157612f91615276565b049350620f424083810387020492505b8487526020870186905260408701849052606087018390526f4b3b4ca85a86c47a098a224000000000841015612fe657612fdc83850261470e565b6080880152613015565b613005670de0b6b3a76400008404670de0b6b3a764000086040261470e565b670de0b6b3a76400000260808801525b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202a60048201526130ed907f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024015b602060405180830381865afa1580156130c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130e89190615102565b6147c0565b60c089015260a08801526040517fb5c736e40000000000000000000000000000000000000000000000000000000081527fa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7600482015261318d907f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024016130a7565b61010089015260e08801525094979650505050505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080613265576040517fdee51a8a000000000000000000000000000000000000000000000000000000008152620115596004820152602401610599565b5050505050565b6040517fb5c736e400000000000000000000000000000000000000000000000000000000815260048101849052600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497169063b5c736e490602401602060405180830381865afa1580156132fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133209190615102565b905067ffffffffffffffff600182901c16915060ff8216600883901c901b9150806001166001036133585764e8d4a510008285020491505b826133b4577f00000000000000000000000000000000000000000000000000000000000f42407f00000000000000000000000000000000000000000000000000000000000000018302816133ae576133ae615276565b04610806565b7f00000000000000000000000000000000000000000000000000000000000f42407f000000000000000000000000000000000000000000000000000000000000000183028161340557613405615276565b0495945050505050565b6000808080806b06765c793fa10079d0000000613438876b033b2e3c9fd0803ce800000061525f565b6134428b8a61525f565b61344c91906153b1565b61345691906153d8565b90506000613464878961525f565b905074446c3b15f9926687d2c40534fdb56400000000000081106134a857886134996b033b2e3c9fd0803ce8000000836152a5565b6134a3919061525f565b6134c9565b6b033b2e3c9fd0803ce80000006134bf8a8361525f565b6134c991906152a5565b90506134e76134d88380615440565b6134e290836152b9565b61470e565b6134f1908361548c565b94506134fd898961525f565b613513866b033b2e3c9fd0803ce800000061525f565b61351d91906151df565b9250620f4240831015613560576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76d6004820152602401610599565b6a084595161401484a0000008510156135a5578261357e868061525f565b613594906b033b2e3c9fd0803ce800000061525f565b61359e91906152a5565b92506135d2565b6135bb6b033b2e3c9fd0803ce8000000846152a5565b6135c5868061525f565b6135cf91906152a5565b92505b87856135de828661525f565b6135e891906152a5565b6135f291906151df565b93506135fe87846152b9565b613608888661525f565b61361291906152a5565b95505050945094509450949050565b620f424082108061363d57506bffffffffffffffffffffffff82115b806136485750606481105b8061366257506fffffffffffffffffffffffffffffffff81115b1561369d576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7696004820152602401610599565b5050565b6000806136c36136b1868861525f565b6134e290670de0b6b3a764000061525f565b905060006136d46136b1858761525f565b90506136e081836152b9565b6136ea828861525f565b6136f4848b61525f565b6136fe858861525f565b61370891906152b9565b61371291906153b1565b61371c91906153d8565b98975050505050505050565b600083820283850180828161373f5761373f615276565b049695505050505050565b613754818361525f565b61376a846b033b2e3c9fd0803ce800000061525f565b61377491906152a5565b8410156137b1576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73e6004820152602401610599565b50505050565b6137cd816b033b2e3c9fd0803ce800000061525f565b6137d7838661525f565b6137e191906152a5565b8310156137b1576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73e6004820152602401610599565b6138487f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be856140ad565b6040517fd1f9be4e00000000000000000000000000000000000000000000000000000000815260048101859052831515602482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb811660448301527f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8116606483015260848201849052919091169063d1f9be4e9060a4016020604051808303816000875af1925050508015613950575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261394d918101906150e5565b60015b61398a573d80801561397e576040519150601f19603f3d011682016040523d82523d6000602084013e613983565b606091505b50506137b1565b80613265576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7446004820152602401610599565b6103e882101561369d576139da82600a61525f565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081526004810183905290925060009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497169063b5c736e490602401602060405180830381865afa158015613a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a8f9190615102565b601e1c613fff16905082811115613ad6576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7436004820152602401610599565b505050565b600080613af3607984901c6401ffffffff16426151df565b9050600080600083600003613bdb5763ffffffff605987901c1660ff605188901c161b92506305f5e1006305f5e0ff840204871080613b3d57506305f5e1006305f5e10184020487115b15613b78576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76c6004820152602401610599565b63ffffffff600987901c1660ff600188901c161b9250613b9883896149ce565b506029613baa89602060086000614a70565b901b867ffffffffffffffffffffffffffffffffffffffffffffe0000000001ffffffffff1617945050505050613f99565b60c386901c600116600003613c755763ffffffff603187901c1660ff602988901c161b9250613c0a83896149ce565b504260791b6051613c2089602060086000614a70565b901b6029613c338b602060086000614a70565b7ffffffffffffffffffffffffffc000000000000000000000000000000000000018a166501fffffffffe60288c901c1617911b1717179450613f999350505050565b63ffffffff600987901c811660ff600189901c81169190911b9450603188901c909116602988901c9091161b91506000613caf838a6149ce565b905082670de0b6b3a7640000850281613cca57613cca615276565b04670de0b6b3a76400000390506000811215613d0a57600166b1a2bc2ec50000623fffff836000030281613d0057613d00615276565b04901b9150613d23565b600166b1a2bc2ec50000623fffff830204901b60011791505b623fffff851115613d3557623fffff94505b609a87901c623fffff16935060b087901c60071660b388901c61ffff166101ff861115613e70578115613e015760299390931b600986901b1792613d788261522a565b9150613d8582602061525f565b600082815260066020526040902080549186901b90911790558115613db457613dad8261522a565b9150613f13565b600791507f00000000000000000000000000000000000000000000000000000000000004008160010181613dea57613dea615276565b066000818152600660205260408120559050613f13565b6000818152600660208190526040909120805460099690961b958617905591507f00000000000000000000000000000000000000000000000000000000000004006001820181613e5357613e53615276565b06600081815260066020526040902060e988901b90559050613f13565b85600985901b1793506007821015613ea5576000818152600660209081526040909120805491840286901b9091179055613ebb565b600081815260066020526040902060e085901b90555b8115613eca57613dad8261522a565b600791507f00000000000000000000000000000000000000000000000000000000000004008160010181613f0057613f00615276565b0660008181526006602052604081205590505b8a955089945088935060b381901b60b083901b609a89901b4260791b6051613f408a602060086000614a70565b901b6029613f538c602060086000614a70565b901b600164ffffffffff60298c901c16901b8a7ffffffffffffffff800000000000000000000000000000000000000000000000116171717171717179750505050505050505b9392505050565b60008080613fae87876151df565b90506000613fbd82600261525f565b613fd3866b033b2e3c9fd0803ce800000061525f565b613fdd888b61525f565b613fe791906152b9565b613ff191906152a5565b90506000613fff868861525f565b905074446c3b15f9926687d2c40534fdb56400000000000081106140425761402783826152a5565b61403d906b033b2e3c9fd0803ce800000061525f565b614063565b82614059826b033b2e3c9fd0803ce800000061525f565b61406391906152a5565b90506140726134d8838061525f565b61407c90836152b9565b94506b033b2e3c9fd0803ce80000006140958a8761525f565b61409f91906152a5565b935050505094509492505050565b60006060826000036140c3576000915050614513565b607f831161418c576040517fd60000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16602282015260f884901b7fff000000000000000000000000000000000000000000000000000000000000001660368201526037015b6040516020818303038152906040529050614508565b60ff8311614269576040517fd70000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f8100000000000000000000000000000000000000000000000000000000000000603682015260f884901b7fff00000000000000000000000000000000000000000000000000000000000000166037820152603801614176565b61ffff8311614347576040517fd80000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f820000000000000000000000000000000000000000000000000000000000000060368201527fffff00000000000000000000000000000000000000000000000000000000000060f085901b166037820152603901614176565b62ffffff8311614426576040517fd90000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f830000000000000000000000000000000000000000000000000000000000000060368201527fffffff000000000000000000000000000000000000000000000000000000000060e885901b166037820152603a01614176565b6040517fda0000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f840000000000000000000000000000000000000000000000000000000000000060368201527fffffffff0000000000000000000000000000000000000000000000000000000060e085901b166037820152603b0160405160208183030381529060405290505b805160209091012090505b92915050565b60405160248101839052604481018290526000906145fb907f0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b907f2f850fb300000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611880565b806020019051810190613f999190615102565b6040516024810184905260448101839052606481018290526000908190819061467d907f0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b907f0d7cf0ab0000000000000000000000000000000000000000000000000000000090608401614579565b80602001905181019061469091906154ac565b92509250925093509350939050565b6040516024810184905260448101839052606481018290526000908190819061467d907f0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b907fe8518a660000000000000000000000000000000000000000000000000000000090608401614579565b60b5817101000000000000000000000000000000000081106147355760409190911b9060801c5b690100000000000000000081106147515760209190911b9060401c5b6501000000000081106147695760109190911b9060201c5b6301000000811061477f5760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b67ffffffffffffffff605b82901c811690609b83901c168115806147e2575080155b1561481e576040517fd50d7512000000000000000000000000000000000000000000000000000000008152620111716004820152602401610599565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480614848575082155b806148535750806001145b1561486057505050915091565b64496cebb80084840283020484019350617fff60db87901c1692508260010361488b57505050915091565b826001166001036148e05760019290921c91826c7e37be2022c0914b2680000000816148b9576148b9615276565b049250612710601e87901c613fff166b033b2e3c9fd0803ce800000085010204925061490d565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b806001166001036149445760011c61271081016b033b2e3c9fd0803ce800000082028161493c5761493c615276565b04905061497a565b60011c61271081016b033b2e3c9fd0803ce800000082028161496857614968615276565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b6000816149e3670de0b6b3a76400008561525f565b6149ed91906152a5565b6149ff90670de0b6b3a76400006153b1565b905066b1a2bc2ec50000811380614a3557507fffffffffffffffffffffffffffffffffffffffffffffffffff4e5d43d13b000081125b15614513576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7606004820152602401610599565b600080856fffffffffffffffffffffffffffffffff811115614a935760809150811c5b67ffffffffffffffff811115614aab576040918201911c5b63ffffffff811115614abf576020918201911c5b61ffff811115614ad1576010918201911c5b60ff811115614ae2576008918201911c5b600f811115614af3576004918201911c5b6003811115614b04576002918201911c5b6001811115614b14576001820191505b8015614b21576001820191505b5084811015614b2d5750835b848103905085811c60008211841615614b7c57600181019050806001871b03614b7c57506001908101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86011b5b6001851b8210614b8b57600080fd5b90931b909201949350505050565b600080600080600060a08688031215614bb157600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b60c081016145138284805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b8151815260208083015190820152604080830151908201526060808301519082015260808101614513565b73ffffffffffffffffffffffffffffffffffffffff81168114614c6457600080fd5b50565b60008060008060808587031215614c7d57600080fd5b8435935060208501359250604085013591506060850135614c9d81614c42565b939692955090935050565b8015158114614c6457600080fd5b60008060008060808587031215614ccc57600080fd5b8435614cd781614ca8565b935060208501359250604085013591506060850135614c9d81614c42565b60008060008060808587031215614d0b57600080fd5b8435935060208501359250604085013591506060850135614c9d81614ca8565b60008060008060608587031215614d4157600080fd5b8435614d4c81614c42565b935060208501359250604085013567ffffffffffffffff80821115614d7057600080fd5b818701915087601f830112614d8457600080fd5b813581811115614d9357600080fd5b886020828501011115614da557600080fd5b95989497505060200194505050565b600060208284031215614dc657600080fd5b5035919050565b81518152602080830151610240830191614dfe9084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151614e26604084018273ffffffffffffffffffffffffffffffffffffffff169052565b506060830151614e84606084018273ffffffffffffffffffffffffffffffffffffffff808251168352806020830151166020840152806040830151166040840152806060830151166060840152806080830151166080840152505050565b506080830151610100614eae8185018373ffffffffffffffffffffffffffffffffffffffff169052565b60a08501519150610120614ed98186018473ffffffffffffffffffffffffffffffffffffffff169052565b60c08601519250610140614f048187018573ffffffffffffffffffffffffffffffffffffffff169052565b60e0870151610160878101919091529287015161018080880191909152918701516101a080880191909152908701516101c0870152918601516101e08601528501516102008501529093015161022090920191909152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020808385031215614fa157600080fd5b823567ffffffffffffffff80821115614fb957600080fd5b818501915085601f830112614fcd57600080fd5b813581811115614fdf57614fdf614f5f565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110858211171561502257615022614f5f565b60405291825284820192508381018501918883111561504057600080fd5b938501935b8285101561371c57843584529385019392850192615045565b604080825283519082018190526000906020906060840190828701845b828110156150d6576150c3848351805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b60c093909301929084019060010161507b565b50505092019290925292915050565b6000602082840312156150f757600080fd5b8151613f9981614ca8565b60006020828403121561511457600080fd5b5051919050565b6000806040838503121561512e57600080fd5b505080516020909101519092909150565b60008060006060848603121561515457600080fd5b83359250602084013561516681614ca8565b9150604084013561517681614c42565b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115614513576145136151b0565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615223576152236151b0565b5060010190565b600081615239576152396151b0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b8082028115828204841417614513576145136151b0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826152b4576152b4615276565b500490565b80820180821115614513576145136151b0565b60007f800000000000000000000000000000000000000000000000000000000000000082036152fd576152fd6151b0565b5060000390565b600073ffffffffffffffffffffffffffffffffffffffff8089168352602088818501528760408501528187166060850152818616608085015260c060a0850152845191508160c085015260005b8281101561536d5785810182015185820160e001528101615351565b5050600060e0828501015260e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116840101915050979650505050505050565b81810360008312801583831316838312821617156153d1576153d16151b0565b5092915050565b6000826153e7576153e7615276565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561543b5761543b6151b0565b500590565b808202600082127f800000000000000000000000000000000000000000000000000000000000000084141615615478576154786151b0565b8181058314821517614513576145136151b0565b80820182811260008312801582168215821617156118be576118be6151b0565b6000806000606084860312156154c157600080fd5b835192506020840151915060408401519050925092509256fea2646970667358221220030a7fecc110b38b34765d7248652e1fcb76dfaaf261eed5e38b5d920a127a9a64736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49700000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c190850000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d80000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d30000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df255430000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a727fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d39853c6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf0806962814311ec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202aa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d70000000000000000000000000000000000000000000000000000000000000400
-----Decoded View---------------
Arg [0] : constantViews_ (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000000000b
Arg [1] : 00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497
Arg [2] : 00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c19085
Arg [3] : 0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b
Arg [4] : 000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d8
Arg [5] : 0000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c
Arg [6] : 000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d3
Arg [7] : 0000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df25543
Arg [8] : 0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be
Arg [9] : 0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb
Arg [10] : 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
Arg [11] : 76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a72
Arg [12] : 7fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d39853
Arg [13] : c6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf0806962814311
Arg [14] : ec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b
Arg [15] : 7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202a
Arg [16] : a1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000400
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.