My Name Tag:
Not Available
Txn Hash | Method |
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0xe5cd7af96c304d8e9578348b857b6fa59c312b5fc5e3a99bfa423689e4a1b7d4 | 0x60808060 | 17185327 | 630 days 23 hrs ago | Rage Trade: Deployer | IN | Create: Account | 0 ETH | 0.016657709184 ETH |
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
Account
Compiler Version
v0.8.14+commit.80d49f37
Optimization Enabled:
Yes with 4999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; import { FixedPoint128 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint128.sol'; import { FullMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/FullMath.sol'; import { SafeCast } from '@uniswap/v3-core-0.8-support/contracts/libraries/SafeCast.sol'; import { AddressHelper } from './AddressHelper.sol'; import { CollateralDeposit } from './CollateralDeposit.sol'; import { SignedFullMath } from './SignedFullMath.sol'; import { SignedMath } from './SignedMath.sol'; import { LiquidityPositionSet } from './LiquidityPositionSet.sol'; import { LiquidityPosition } from './LiquidityPosition.sol'; import { Protocol } from './Protocol.sol'; import { VTokenPosition } from './VTokenPosition.sol'; import { VTokenPositionSet } from './VTokenPositionSet.sol'; import { IClearingHouseStructures } from '../interfaces/clearinghouse/IClearingHouseStructures.sol'; import { IClearingHouseEnums } from '../interfaces/clearinghouse/IClearingHouseEnums.sol'; import { IVQuote } from '../interfaces/IVQuote.sol'; import { IVToken } from '../interfaces/IVToken.sol'; import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; /// @title Cross margined account functions /// @dev This library is deployed and used as an external library by ClearingHouse contract. library Account { using AddressHelper for address; using FullMath for uint256; using SafeCast for uint256; using SignedFullMath for int256; using SignedMath for int256; using Account for Account.Info; using CollateralDeposit for CollateralDeposit.Set; using LiquidityPositionSet for LiquidityPosition.Set; using Protocol for Protocol.Info; using VTokenPosition for VTokenPosition.Info; using VTokenPositionSet for VTokenPosition.Set; /// @notice account info for user /// @param owner specifies the account owner /// @param tokenPositions is set of all open token positions /// @param collateralDeposits is set of all deposits struct Info { uint96 id; address owner; VTokenPosition.Set tokenPositions; CollateralDeposit.Set collateralDeposits; uint256[100] _emptySlots; // reserved for adding variables when upgrading logic } /** * Errors */ /// @notice error to denote that there is not enough margin for the transaction to go through /// @param accountMarketValue shows the account market value after the transaction is executed /// @param totalRequiredMargin shows the total required margin after the transaction is executed error InvalidTransactionNotEnoughMargin(int256 accountMarketValue, int256 totalRequiredMargin); /// @notice error to denote that there is not enough profit during profit withdrawal /// @param totalProfit shows the value of positions at the time of execution after removing amount specified error InvalidTransactionNotEnoughProfit(int256 totalProfit); /// @notice error to denote that there is enough margin, hence the liquidation is invalid /// @param accountMarketValue shows the account market value before liquidation /// @param totalRequiredMargin shows the total required margin before liquidation error InvalidLiquidationAccountAboveWater(int256 accountMarketValue, int256 totalRequiredMargin); /// @notice error to denote that there are active ranges present during token liquidation, hence the liquidation is invalid /// @param poolId shows the poolId for which range is active error InvalidLiquidationActiveRangePresent(uint32 poolId); /// @notice denotes withdrawal of profit in settlement token /// @param accountId serial number of the account /// @param amount amount of profit withdrawn event ProfitUpdated(uint256 indexed accountId, int256 amount); /** * Events */ /// @notice denotes add or remove of margin /// @param accountId serial number of the account /// @param collateralId token in which margin is deposited /// @param amount amount of tokens deposited event MarginUpdated(uint256 indexed accountId, uint32 indexed collateralId, int256 amount, bool isSettleProfit); /// @notice denotes range position liquidation event /// @dev all range positions are liquidated and the current tokens inside the range are added in as token positions to the account /// @param accountId serial number of the account /// @param keeperAddress address of keeper who performed the liquidation /// @param liquidationFee total liquidation fee charged to the account /// @param keeperFee total liquidaiton fee paid to the keeper (positive only) /// @param insuranceFundFee total liquidaiton fee paid to the insurance fund (can be negative in case the account is not enought to cover the fee) event LiquidityPositionsLiquidated( uint256 indexed accountId, address indexed keeperAddress, int256 liquidationFee, int256 keeperFee, int256 insuranceFundFee, int256 accountMarketValueFinal ); /// @notice denotes token position liquidation event /// @dev the selected token position is take from the current account and moved to liquidatorAccount at a discounted prive to current pool price /// @param accountId serial number of the account /// @param poolId id of the rage trade pool for whose position was liquidated /// @param keeperFee total liquidaiton fee paid to keeper /// @param insuranceFundFee total liquidaiton fee paid to the insurance fund (can be negative in case the account is not enough to cover the fee) event TokenPositionLiquidated( uint256 indexed accountId, uint32 indexed poolId, int256 keeperFee, int256 insuranceFundFee, int256 accountMarketValueFinal ); /** * External methods */ /// @notice changes deposit balance of 'vToken' by 'amount' /// @param account account to deposit balance into /// @param collateralId collateral id of the token /// @param amount amount of token to deposit or withdraw /// @param protocol set of all constants and token addresses /// @param checkMargin true to check if margin is available else false function updateMargin( Account.Info storage account, uint32 collateralId, int256 amount, Protocol.Info storage protocol, bool checkMargin ) external { _updateMargin(account, collateralId, amount, protocol, checkMargin, false); } /// @notice updates 'amount' of profit generated in settlement token /// @param account account to remove profit from /// @param amount amount of profit(settlement token) to add/remove /// @param protocol set of all constants and token addresses /// @param checkMargin true to check if margin is available else false function updateProfit( Account.Info storage account, int256 amount, Protocol.Info storage protocol, bool checkMargin ) external { _updateProfit(account, amount, protocol, checkMargin); } function settleProfit(Account.Info storage account, Protocol.Info storage protocol) external { _settleProfit(account, protocol); } /// @notice swaps 'vToken' of token amount equal to 'swapParams.amount' /// @notice if vTokenAmount>0 then the swap is a long or close short and if vTokenAmount<0 then swap is a short or close long /// @notice isNotional specifies whether the amount represents token amount (false) or vQuote amount(true) /// @notice isPartialAllowed specifies whether to revert (false) or to execute a partial swap (true) /// @notice sqrtPriceLimit threshold sqrt price which if crossed then revert or execute partial swap /// @param account account to swap tokens for /// @param poolId id of the pool to swap tokens for /// @param swapParams parameters for the swap (Includes - amount, sqrtPriceLimit, isNotional, isPartialAllowed) /// @param protocol set of all constants and token addresses /// @param checkMargin true to check if margin is available else false /// @return vTokenAmountOut amount of vToken after swap (user receiving then +ve, user paying then -ve) /// @return vQuoteAmountOut amount of vQuote after swap (user receiving then +ve, user paying then -ve) function swapToken( Account.Info storage account, uint32 poolId, IClearingHouseStructures.SwapParams memory swapParams, Protocol.Info storage protocol, bool checkMargin ) external returns (int256 vTokenAmountOut, int256 vQuoteAmountOut) { // make a swap. vQuoteIn and vTokenAmountOut (in and out wrt uniswap). // mints erc20 tokens in callback and send to the pool (vTokenAmountOut, vQuoteAmountOut) = account.tokenPositions.swapToken(account.id, poolId, swapParams, protocol); if (swapParams.settleProfit) { account._settleProfit(protocol); } // after all the stuff, account should be above water if (checkMargin) account._checkIfMarginAvailable(true, protocol); } /// @notice changes range liquidity 'vToken' of market value equal to 'vTokenNotional' /// @notice if 'liquidityDelta'>0 then liquidity is added and if 'liquidityChange'<0 then liquidity is removed /// @notice the liquidity change is reverted if the sqrt price at the time of execution is beyond 'slippageToleranceBps' of 'sqrtPriceCurrent' supplied /// @notice whenever liquidity change is done the external token position is taken out. If 'closeTokenPosition' is true this is swapped out else it is added to the current token position /// @param account account to change liquidity /// @param poolId id of the rage trade pool /// @param liquidityChangeParams parameters including lower tick, upper tick, liquidity delta, sqrtPriceCurrent, slippageToleranceBps, closeTokenPosition, limit order type /// @param protocol set of all constants and token addresses function liquidityChange( Account.Info storage account, uint32 poolId, IClearingHouseStructures.LiquidityChangeParams memory liquidityChangeParams, Protocol.Info storage protocol, bool checkMargin ) external returns ( int256 vTokenAmountOut, int256 vQuoteAmountOut, uint256 notionalValueAbs ) { // mint/burn tokens + fee + funding payment (vTokenAmountOut, vQuoteAmountOut) = account.tokenPositions.liquidityChange( account.id, poolId, liquidityChangeParams, protocol ); if (liquidityChangeParams.settleProfit) { account._settleProfit(protocol); } // after all the stuff, account should be above water if (checkMargin) account._checkIfMarginAvailable(true, protocol); notionalValueAbs = protocol.getNotionalValue(poolId, vTokenAmountOut, vQuoteAmountOut); } /// @notice liquidates all range positions in case the account is under water /// charges a liquidation fee to the account and pays partially to the insurance fund and rest to the keeper. /// @dev insurance fund covers the remaining fee if the account market value is not enough /// @param account account to liquidate /// @param protocol set of all constants and token addresses /// @return keeperFee amount of liquidation fee paid to keeper /// @return insuranceFundFee amount of liquidation fee paid to insurance fund /// @return accountMarketValue account market value before liquidation function liquidateLiquidityPositions(Account.Info storage account, Protocol.Info storage protocol) external returns ( int256 keeperFee, int256 insuranceFundFee, int256 accountMarketValue ) { // check basis maintanace margin int256 totalRequiredMargin; uint256 notionalAmountClosed; (accountMarketValue, totalRequiredMargin) = account._getAccountValueAndRequiredMargin(false, protocol); // check and revert if account is above water if (accountMarketValue > totalRequiredMargin) { revert InvalidLiquidationAccountAboveWater(accountMarketValue, totalRequiredMargin); } // liquidate all liquidity positions notionalAmountClosed = account.tokenPositions.liquidateLiquidityPositions(account.id, protocol); // compute liquidation fees (keeperFee, insuranceFundFee) = _computeLiquidationFees( accountMarketValue, notionalAmountClosed, true, protocol.liquidationParams ); account._updateVQuoteBalance(-(keeperFee + insuranceFundFee)); } /// @notice liquidates token position specified by 'poolId' in case account is underwater /// charges a liquidation fee to the account and pays partially to the insurance fund and rest to the keeper. /// @dev closes position uptil a specified slippage threshold in protocol.liquidationParams /// @dev insurance fund covers the remaining fee if the account market value is not enough /// @dev if there is range position this reverts (liquidators are supposed to liquidate range positions first) /// @param account account to liquidate /// @param poolId id of the pool to liquidate /// @param protocol set of all constants and token addresses /// @return keeperFee amount of liquidation fee paid to keeper /// @return insuranceFundFee amount of liquidation fee paid to insurance fund function liquidateTokenPosition( Account.Info storage account, uint32 poolId, Protocol.Info storage protocol ) external returns (int256 keeperFee, int256 insuranceFundFee) { bool isPartialLiquidation; // check if there is range position and revert if (account.tokenPositions.isTokenRangeActive(poolId)) revert InvalidLiquidationActiveRangePresent(poolId); { (int256 accountMarketValue, int256 totalRequiredMargin) = account._getAccountValueAndRequiredMargin( false, protocol ); // check and revert if account is above water if (accountMarketValue > totalRequiredMargin) { revert InvalidLiquidationAccountAboveWater(accountMarketValue, totalRequiredMargin); } else if ( // check if account is underwater but within partial liquidation threshold accountMarketValue > totalRequiredMargin.mulDiv(protocol.liquidationParams.closeFactorMMThresholdBps, 1e4) ) { isPartialLiquidation = true; } } int256 tokensToTrade; { // get the net token position and tokensToTrade = -tokenPosition // since no ranges are supposed to be there so only tokenPosition is in vTokenPositionSet VTokenPosition.Info storage vTokenPosition = account.tokenPositions.getTokenPosition(poolId, false); tokensToTrade = -vTokenPosition.balance; uint256 tokenNotionalValue = tokensToTrade.absUint().mulDiv( protocol.getCachedVirtualTwapPriceX128(poolId), FixedPoint128.Q128 ); // check if the token position is less than a certain notional value // if so then liquidate the whole position even if partial liquidation is allowed // otherwise do partial liquidation if (isPartialLiquidation && tokenNotionalValue > protocol.liquidationParams.minNotionalLiquidatable) { tokensToTrade = tokensToTrade.mulDiv(protocol.liquidationParams.partialLiquidationCloseFactorBps, 1e4); } } int256 accountMarketValueFinal; { uint160 sqrtPriceLimit; { // calculate sqrt price limit based on slippage threshold uint160 sqrtTwapPrice = protocol.getVirtualTwapSqrtPriceX96(poolId); if (tokensToTrade > 0) { sqrtPriceLimit = uint256(sqrtTwapPrice) .mulDiv(1e4 + protocol.liquidationParams.liquidationSlippageSqrtToleranceBps, 1e4) .toUint160(); } else { sqrtPriceLimit = uint256(sqrtTwapPrice) .mulDiv(1e4 - protocol.liquidationParams.liquidationSlippageSqrtToleranceBps, 1e4) .toUint160(); } } // close position uptil sqrt price limit (, int256 vQuoteAmountSwapped) = account.tokenPositions.swapToken( account.id, poolId, IClearingHouseStructures.SwapParams({ amount: tokensToTrade, sqrtPriceLimit: sqrtPriceLimit, isNotional: false, isPartialAllowed: true, settleProfit: false }), protocol ); // get the account market value after closing the position accountMarketValueFinal = account._getAccountValue(protocol); // compute liquidation fees (keeperFee, insuranceFundFee) = _computeLiquidationFees( accountMarketValueFinal, vQuoteAmountSwapped.absUint(), false, protocol.liquidationParams ); } // deduct liquidation fees from account account._updateVQuoteBalance(-(keeperFee + insuranceFundFee)); emit TokenPositionLiquidated(account.id, poolId, keeperFee, insuranceFundFee, accountMarketValueFinal); } /// @notice removes limit order based on the current price position (keeper call) /// @param account account to liquidate /// @param poolId id of the pool for the range /// @param tickLower lower tick index for the range /// @param tickUpper upper tick index for the range /// @param protocol platform constants function removeLimitOrder( Account.Info storage account, uint32 poolId, int24 tickLower, int24 tickUpper, uint256 limitOrderFee, Protocol.Info storage protocol ) external { account.tokenPositions.removeLimitOrder(account.id, poolId, tickLower, tickUpper, protocol); account._updateVQuoteBalance(-int256(limitOrderFee)); } /** * External view methods */ /// @notice returns market value for the account positions based on current market conditions /// @param account account to check /// @param protocol set of all constants and token addresses /// @return accountPositionProfits total market value of all the positions (token ) and deposits function getAccountPositionProfits(Account.Info storage account, Protocol.Info storage protocol) external view returns (int256 accountPositionProfits) { return account._getAccountPositionProfits(protocol); } /// @notice returns market value and required margin for the account based on current market conditions /// @dev (In case requiredMargin < minRequiredMargin then requiredMargin = minRequiredMargin) /// @param account account to check /// @param isInitialMargin true to use initial margin factor and false to use maintainance margin factor for calcualtion of required margin /// @param protocol set of all constants and token addresses /// @return accountMarketValue total market value of all the positions (token ) and deposits /// @return totalRequiredMargin total margin required to keep the account above selected margin requirement (intial/maintainance) function getAccountValueAndRequiredMargin( Account.Info storage account, bool isInitialMargin, Protocol.Info storage protocol ) external view returns (int256 accountMarketValue, int256 totalRequiredMargin) { return account._getAccountValueAndRequiredMargin(isInitialMargin, protocol); } /// @notice checks if market value > required margin else revert with InvalidTransactionNotEnoughMargin /// @param account account to check /// @param isInitialMargin true to use initialMarginFactor and false to use maintainance margin factor for calcualtion of required margin /// @param protocol set of all constants and token addresses function checkIfMarginAvailable( Account.Info storage account, bool isInitialMargin, Protocol.Info storage protocol ) external view { (int256 accountMarketValue, int256 totalRequiredMargin) = account._getAccountValueAndRequiredMargin( isInitialMargin, protocol ); if (accountMarketValue < totalRequiredMargin) revert InvalidTransactionNotEnoughMargin(accountMarketValue, totalRequiredMargin); } /// @notice checks if profit is available to withdraw settlement token (token value of all positions > 0) else revert with InvalidTransactionNotEnoughProfit /// @param account account to check /// @param protocol set of all constants and token addresses function checkIfProfitAvailable(Account.Info storage account, Protocol.Info storage protocol) external view { _checkIfProfitAvailable(account, protocol); } /// @notice gets information about all the collateral and positions in the account /// @param account ref to the account state /// @param protocol ref to the protocol state /// @return owner of the account /// @return vQuoteBalance amount of vQuote in the account /// @return collateralDeposits list of all the collateral amounts /// @return tokenPositions list of all the token and liquidity positions function getInfo(Account.Info storage account, Protocol.Info storage protocol) external view returns ( address owner, int256 vQuoteBalance, IClearingHouseStructures.CollateralDepositView[] memory collateralDeposits, IClearingHouseStructures.VTokenPositionView[] memory tokenPositions ) { owner = account.owner; collateralDeposits = account.collateralDeposits.getInfo(protocol); (vQuoteBalance, tokenPositions) = account.tokenPositions.getInfo(); } /// @notice gets the net position of the account for a given pool /// @param account ref to the account state /// @param poolId id of the pool /// @param protocol ref to the protocol state /// @return netPosition net position of the account for the pool function getNetPosition( Account.Info storage account, uint32 poolId, Protocol.Info storage protocol ) external view returns (int256 netPosition) { return account.tokenPositions.getNetPosition(poolId, protocol); } /** * Internal methods */ function updateAccountPoolPrices(Account.Info storage account, Protocol.Info storage protocol) internal { account.tokenPositions.updateOpenPoolPrices(protocol); } /// @notice settles profit or loss for the account /// @param account ref to the account state /// @param protocol ref to the protocol state function _settleProfit(Account.Info storage account, Protocol.Info storage protocol) internal { int256 profits = account._getAccountPositionProfits(protocol); uint32 settlementCollateralId = AddressHelper.truncate(protocol.settlementToken); if (profits > 0) { account._updateProfit(-profits, protocol, false); account._updateMargin({ collateralId: settlementCollateralId, amount: profits, protocol: protocol, checkMargin: false, isSettleProfit: true }); } else if (profits < 0) { uint256 balance = account.collateralDeposits.getBalance(settlementCollateralId); uint256 profitAbsUint = uint256(-profits); uint256 balanceToUpdate = balance > profitAbsUint ? profitAbsUint : balance; if (balanceToUpdate > 0) { account._updateMargin({ collateralId: settlementCollateralId, amount: -balanceToUpdate.toInt256(), protocol: protocol, checkMargin: false, isSettleProfit: true }); account._updateProfit(balanceToUpdate.toInt256(), protocol, false); } } } /// @notice updates 'amount' of profit generated in settlement token /// @param account account to remove profit from /// @param amount amount of profit(settlement token) to add/remove /// @param protocol set of all constants and token addresses /// @param checkMargin true to check if margin is available else false function _updateProfit( Account.Info storage account, int256 amount, Protocol.Info storage protocol, bool checkMargin ) internal { account._updateVQuoteBalance(amount); if (checkMargin && amount < 0) { account._checkIfProfitAvailable(protocol); account._checkIfMarginAvailable(true, protocol); } emit ProfitUpdated(account.id, amount); } /// @notice changes deposit balance of 'vToken' by 'amount' /// @param account account to deposit balance into /// @param collateralId collateral id of the token /// @param amount amount of token to deposit or withdraw /// @param protocol set of all constants and token addresses /// @param checkMargin true to check if margin is available else false function _updateMargin( Account.Info storage account, uint32 collateralId, int256 amount, Protocol.Info storage protocol, bool checkMargin, bool isSettleProfit ) internal { if (amount > 0) { account.collateralDeposits.increaseBalance(collateralId, uint256(amount)); } else { account.collateralDeposits.decreaseBalance(collateralId, uint256(-amount)); if (checkMargin) account._checkIfMarginAvailable(true, protocol); } emit MarginUpdated(account.id, collateralId, amount, isSettleProfit); } /// @notice updates the vQuote balance for 'account' by 'amount' /// @param account pointer to 'account' struct /// @param amount amount of balance to update /// @return balanceAdjustments vToken and vQuote balance changes of the account function _updateVQuoteBalance(Account.Info storage account, int256 amount) internal returns (IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments) { balanceAdjustments = IClearingHouseStructures.BalanceAdjustments(amount, 0, 0); account.tokenPositions.vQuoteBalance += balanceAdjustments.vQuoteIncrease; } /** * Internal view methods */ /// @notice ensures that the account has enough margin to cover the required margin /// @param account ref to the account state /// @param protocol ref to the protocol state function _checkIfMarginAvailable( Account.Info storage account, bool isInitialMargin, Protocol.Info storage protocol ) internal view { (int256 accountMarketValue, int256 totalRequiredMargin) = account._getAccountValueAndRequiredMargin( isInitialMargin, protocol ); if (accountMarketValue < totalRequiredMargin) revert InvalidTransactionNotEnoughMargin(accountMarketValue, totalRequiredMargin); } /// @notice ensures that the account has non negative profit /// @param account ref to the account state /// @param protocol ref to the protocol state function _checkIfProfitAvailable(Account.Info storage account, Protocol.Info storage protocol) internal view { int256 totalPositionValue = account._getAccountPositionProfits(protocol); if (totalPositionValue < 0) revert InvalidTransactionNotEnoughProfit(totalPositionValue); } /// @notice gets the amount of account's position profits /// @param account ref to the account state /// @param protocol ref to the protocol state function _getAccountPositionProfits(Account.Info storage account, Protocol.Info storage protocol) internal view returns (int256 accountPositionProfits) { accountPositionProfits = account.tokenPositions.getAccountMarketValue(protocol); } /// @notice gets market value for the account based on current market conditions /// @param account ref to the account state /// @param protocol set of all constants and token addresses /// @return accountMarketValue total market value of all the positions (token ) and deposits function _getAccountValue(Account.Info storage account, Protocol.Info storage protocol) internal view returns (int256 accountMarketValue) { accountMarketValue = account._getAccountPositionProfits(protocol); accountMarketValue += account.collateralDeposits.marketValue(protocol); return (accountMarketValue); } /// @notice gets market value and req margin for the account based on current market conditions /// @param account ref to the account state /// @param isInitialMargin true to use initialMarginFactor and false to use maintainance margin factor for calcualtion of required margin /// @param protocol set of all constants and token addresses /// @return accountMarketValue total market value of all the positions (token) and deposits /// @return totalRequiredMargin total required margin for the account function _getAccountValueAndRequiredMargin( Account.Info storage account, bool isInitialMargin, Protocol.Info storage protocol ) internal view returns (int256 accountMarketValue, int256 totalRequiredMargin) { accountMarketValue = account._getAccountValue(protocol); totalRequiredMargin = account.tokenPositions.getRequiredMargin(isInitialMargin, protocol); if (!account.tokenPositions.isEmpty()) { totalRequiredMargin = totalRequiredMargin < int256(protocol.minRequiredMargin) ? int256(protocol.minRequiredMargin) : totalRequiredMargin; } return (accountMarketValue, totalRequiredMargin); } /// @notice checks if 'account' is initialized /// @param account pointer to 'account' struct function _isInitialized(Account.Info storage account) internal view returns (bool) { return !account.owner.isZero(); } /** * Internal pure methods */ /// @notice computes keeper fee and insurance fund fee in case of liquidity position liquidation /// @dev keeperFee = liquidationFee*(1-insuranceFundFeeShare) /// @dev insuranceFundFee = accountMarketValue - keeperFee (if accountMarketValue is not enough to cover the fees) else insurancFundFee = liquidationFee - keeperFee /// @param accountMarketValue market value of account /// @param notionalAmountClosed notional value of position closed /// @param isRangeLiquidation - true for range liquidation and false for token liquidation /// @param liquidationParams parameters including insuranceFundFeeShareBps /// @return keeperFee map of vTokens allowed on the platform /// @return insuranceFundFee poolwrapper for token function _computeLiquidationFees( int256 accountMarketValue, uint256 notionalAmountClosed, bool isRangeLiquidation, IClearingHouseStructures.LiquidationParams memory liquidationParams ) internal pure returns (int256 keeperFee, int256 insuranceFundFee) { uint256 liquidationFee; if (isRangeLiquidation) { liquidationFee = notionalAmountClosed.mulDiv(liquidationParams.rangeLiquidationFeeFraction, 1e5); if (liquidationParams.maxRangeLiquidationFees < liquidationFee) liquidationFee = liquidationParams.maxRangeLiquidationFees; } else { liquidationFee = notionalAmountClosed.mulDiv(liquidationParams.tokenLiquidationFeeFraction, 1e5); } int256 liquidationFeeInt = liquidationFee.toInt256(); keeperFee = liquidationFeeInt.mulDiv(1e4 - liquidationParams.insuranceFundFeeShareBps, 1e4); if (accountMarketValue - liquidationFeeInt < 0) { insuranceFundFee = accountMarketValue - keeperFee; } else { insuranceFundFee = liquidationFeeInt - keeperFee; } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.4.0; /// @title FixedPoint128 /// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) library FixedPoint128 { uint256 internal constant Q128 = 0x100000000000000000000000000000000; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Contains 512-bit math functions /// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision /// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits library FullMath { /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv function mulDiv( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = a * b // Compute the product mod 2**256 and mod 2**256 - 1 // then use the Chinese Remainder Theorem to reconstruct // the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2**256 + prod0 uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(a, b, not(0)) prod0 := mul(a, b) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { require(denominator > 0); assembly { result := div(prod0, denominator) } return result; } // Make sure the result is less than 2**256. // Also prevents denominator == 0 require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0] // Compute remainder using mulmod uint256 remainder; assembly { remainder := mulmod(a, b, denominator) } // Subtract 256 bit number from 512 bit number assembly { prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator // Compute largest power of two divisor of denominator. // Always >= 1. uint256 twos = (0 - denominator) & denominator; // Divide denominator by power of two assembly { denominator := div(denominator, twos) } // Divide [prod1 prod0] by the factors of two assembly { prod0 := div(prod0, twos) } // Shift in bits from prod1 into prod0. For this we need // to flip `twos` such that it is 2**256 / twos. // If twos is zero, then it becomes one assembly { twos := add(div(sub(0, twos), twos), 1) } prod0 |= prod1 * twos; // Invert denominator mod 2**256 // Now that denominator is an odd number, it has an inverse // modulo 2**256 such that denominator * inv = 1 mod 2**256. // Compute the inverse by starting with a seed that is correct // correct for four bits. That is, denominator * inv = 1 mod 2**4 uint256 inv = (3 * denominator) ^ 2; // Now use Newton-Raphson iteration to improve the precision. // Thanks to Hensel's lifting lemma, this also works in modular // arithmetic, doubling the correct bits in each step. inv *= 2 - denominator * inv; // inverse mod 2**8 inv *= 2 - denominator * inv; // inverse mod 2**16 inv *= 2 - denominator * inv; // inverse mod 2**32 inv *= 2 - denominator * inv; // inverse mod 2**64 inv *= 2 - denominator * inv; // inverse mod 2**128 inv *= 2 - denominator * inv; // inverse mod 2**256 // Because the division is now exact we can divide by multiplying // with the modular inverse of denominator. This will give us the // correct result modulo 2**256. Since the precoditions guarantee // that the outcome is less than 2**256, this is the final result. // We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inv; return result; } } /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result function mulDivRoundingUp( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { unchecked { result = mulDiv(a, b, denominator); if (mulmod(a, b, denominator) > 0) { require(result < type(uint256).max); result++; } } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Safe casting methods /// @notice Contains methods for safely casting between types library SafeCast { /// @notice Cast a uint256 to a uint160, revert on overflow /// @param y The uint256 to be downcasted /// @return z The downcasted integer, now type uint160 function toUint160(uint256 y) internal pure returns (uint160 z) { require((z = uint160(y)) == y); } /// @notice Cast a int256 to a int128, revert on overflow or underflow /// @param y The int256 to be downcasted /// @return z The downcasted integer, now type int128 function toInt128(int256 y) internal pure returns (int128 z) { require((z = int128(y)) == y); } /// @notice Cast a uint256 to a int256, revert on overflow /// @param y The uint256 to be casted /// @return z The casted integer, now type int256 function toInt256(uint256 y) internal pure returns (int256 z) { require(y < 2**255); z = int256(y); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import { IVToken } from '../interfaces/IVToken.sol'; /// @title Address helper functions library AddressHelper { /// @notice converts address to uint32, using the least significant 32 bits /// @param addr Address to convert /// @return truncated last 4 bytes of the address function truncate(address addr) internal pure returns (uint32 truncated) { assembly { truncated := and(addr, 0xffffffff) } } /// @notice converts IERC20 contract to uint32 /// @param addr contract /// @return truncated last 4 bytes of the address function truncate(IERC20 addr) internal pure returns (uint32 truncated) { return truncate(address(addr)); } /// @notice checks if two addresses are equal /// @param a first address /// @param b second address /// @return true if addresses are equal function eq(address a, address b) internal pure returns (bool) { return a == b; } /// @notice checks if addresses of two IERC20 contracts are equal /// @param a first contract /// @param b second contract /// @return true if addresses are equal function eq(IERC20 a, IERC20 b) internal pure returns (bool) { return eq(address(a), address(b)); } /// @notice checks if an address is zero /// @param a address to check /// @return true if address is zero function isZero(address a) internal pure returns (bool) { return a == address(0); } /// @notice checks if address of an IERC20 contract is zero /// @param a contract to check /// @return true if address is zero function isZero(IERC20 a) internal pure returns (bool) { return isZero(address(a)); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.4; import { FixedPoint128 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint128.sol'; import { SafeCast } from '@uniswap/v3-core-0.8-support/contracts/libraries/SafeCast.sol'; import { Protocol } from './Protocol.sol'; import { AddressHelper } from './AddressHelper.sol'; import { SignedFullMath } from './SignedFullMath.sol'; import { Uint32L8ArrayLib } from './Uint32L8Array.sol'; import { IClearingHouseStructures } from '../interfaces/clearinghouse/IClearingHouseStructures.sol'; /// @title Collateral deposit set functions library CollateralDeposit { using AddressHelper for address; using SafeCast for uint256; using SignedFullMath for int256; using Uint32L8ArrayLib for uint32[8]; error InsufficientCollateralBalance(); struct Set { // Fixed length array of collateralId = collateralAddress.truncate() // Supports upto 8 different collaterals in an account. // Collision is possible, i.e. collateralAddress1.truncate() == collateralAddress2.truncate() // However the possibility is 1/2**32, which is negligible. // There are checks that prevent use of a different collateralAddress for a given collateralId. // If there is a geniune collision, a wrapper for the ERC20 token can deployed such that // there are no collisions with wrapper and the wrapped ERC20 can be used as collateral. uint32[8] active; // array of collateralIds mapping(uint32 => uint256) deposits; // collateralId => deposit amount uint256[100] _emptySlots; // reserved for adding variables when upgrading logic } function getBalance(CollateralDeposit.Set storage set, uint32 collateralId) internal view returns (uint256) { return set.deposits[collateralId]; } /// @notice Increase the deposit amount of a given collateralId /// @param set CollateralDepositSet of the account /// @param collateralId The collateralId of the collateral to increase the deposit amount of /// @param amount The amount to increase the deposit amount of the collateral by function increaseBalance( CollateralDeposit.Set storage set, uint32 collateralId, uint256 amount ) internal { set.active.include(collateralId); set.deposits[collateralId] += amount; } /// @notice Decrease the deposit amount of a given collateralId /// @param set CollateralDepositSet of the account /// @param collateralId The collateralId of the collateral to decrease the deposit amount of /// @param amount The amount to decrease the deposit amount of the collateral by function decreaseBalance( CollateralDeposit.Set storage set, uint32 collateralId, uint256 amount ) internal { if (set.deposits[collateralId] < amount) revert InsufficientCollateralBalance(); set.deposits[collateralId] -= amount; if (set.deposits[collateralId] == 0) { set.active.exclude(collateralId); } } /// @notice Get the market value of all the collateral deposits in settlementToken denomination /// @param set CollateralDepositSet of the account /// @param protocol Global protocol state /// @return The market value of all the collateral deposits in settlementToken denomination function marketValue(CollateralDeposit.Set storage set, Protocol.Info storage protocol) internal view returns (int256) { int256 accountMarketValue; for (uint8 i = 0; i < set.active.length; i++) { uint32 collateralId = set.active[i]; if (collateralId == 0) break; IClearingHouseStructures.Collateral storage collateral = protocol.collaterals[collateralId]; accountMarketValue += set.deposits[collateralId].toInt256().mulDiv( collateral.settings.oracle.getTwapPriceX128(collateral.settings.twapDuration), FixedPoint128.Q128 ); } return accountMarketValue; } /// @notice Get information about all the collateral deposits /// @param set CollateralDepositSet of the account /// @param protocol Global protocol state /// @return collateralDeposits Information about all the collateral deposits function getInfo(CollateralDeposit.Set storage set, Protocol.Info storage protocol) internal view returns (IClearingHouseStructures.CollateralDepositView[] memory collateralDeposits) { uint256 numberOfTokenPositions = set.active.numberOfNonZeroElements(); collateralDeposits = new IClearingHouseStructures.CollateralDepositView[](numberOfTokenPositions); for (uint256 i = 0; i < numberOfTokenPositions; i++) { collateralDeposits[i].collateral = protocol.collaterals[set.active[i]].token; collateralDeposits[i].balance = set.deposits[set.active[i]]; } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import { FullMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/FullMath.sol'; import { SafeCast } from '@uniswap/v3-core-0.8-support/contracts/libraries/SafeCast.sol'; import { SignedMath } from './SignedMath.sol'; /// @title Signed full math functions library SignedFullMath { using SafeCast for uint256; using SignedMath for int256; /// @notice uses full math on signed int and two unsigned ints /// @param a: signed int /// @param b: unsigned int to be multiplied by /// @param denominator: unsigned int to be divided by /// @return result of a * b / denominator function mulDiv( int256 a, uint256 b, uint256 denominator ) internal pure returns (int256 result) { result = FullMath.mulDiv(a < 0 ? uint256(-1 * a) : uint256(a), b, denominator).toInt256(); if (a < 0) { result = -result; } } /// @notice uses full math on three signed ints /// @param a: signed int /// @param b: signed int to be multiplied by /// @param denominator: signed int to be divided by /// @return result of a * b / denominator function mulDiv( int256 a, int256 b, int256 denominator ) internal pure returns (int256 result) { bool resultPositive = true; uint256 _a; uint256 _b; uint256 _denominator; (_a, resultPositive) = a.extractSign(resultPositive); (_b, resultPositive) = b.extractSign(resultPositive); (_denominator, resultPositive) = denominator.extractSign(resultPositive); result = FullMath.mulDiv(_a, _b, _denominator).toInt256(); if (!resultPositive) { result = -result; } } /// @notice rounds down towards negative infinity /// @dev in Solidity -3/2 is -1. But this method result is -2 /// @param a: signed int /// @param b: unsigned int to be multiplied by /// @param denominator: unsigned int to be divided by /// @return result of a * b / denominator rounded towards negative infinity function mulDivRoundingDown( int256 a, uint256 b, uint256 denominator ) internal pure returns (int256 result) { result = mulDiv(a, b, denominator); if (result < 0 && _hasRemainder(a.absUint(), b, denominator)) { result += -1; } } /// @notice rounds down towards negative infinity /// @dev in Solidity -3/2 is -1. But this method result is -2 /// @param a: signed int /// @param b: signed int to be multiplied by /// @param denominator: signed int to be divided by /// @return result of a * b / denominator rounded towards negative infinity function mulDivRoundingDown( int256 a, int256 b, int256 denominator ) internal pure returns (int256 result) { result = mulDiv(a, b, denominator); if (result < 0 && _hasRemainder(a.absUint(), b.absUint(), denominator.absUint())) { result += -1; } } /// @notice checks if full multiplication of a & b would have a remainder if divided by denominator /// @param a: multiplicand /// @param b: multiplier /// @param denominator: divisor /// @return hasRemainder true if there is a remainder, false otherwise function _hasRemainder( uint256 a, uint256 b, uint256 denominator ) private pure returns (bool hasRemainder) { assembly { let remainder := mulmod(a, b, denominator) if gt(remainder, 0) { hasRemainder := 1 } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; int256 constant ONE = 1; /// @title Signed math functions library SignedMath { /// @notice gives the absolute value of a signed int /// @param value signed int /// @return absolute value of signed int function abs(int256 value) internal pure returns (int256) { return value > 0 ? value : -value; } /// @notice gives the absolute value of a signed int /// @param value signed int /// @return absolute value of signed int as unsigned int function absUint(int256 value) internal pure returns (uint256) { return uint256(abs(value)); } /// @notice gives the sign of a signed int /// @param value signed int /// @return -1 if negative, 1 if non-negative function sign(int256 value) internal pure returns (int256) { return value >= 0 ? ONE : -ONE; } /// @notice converts a signed integer into an unsigned integer and inverts positive bool if negative /// @param a signed int /// @param positive initial value of positive bool /// @return _a absolute value of int provided /// @return bool xor of the positive boolean and sign of the provided int function extractSign(int256 a, bool positive) internal pure returns (uint256 _a, bool) { if (a < 0) { positive = !positive; _a = uint256(-a); } else { _a = uint256(a); } return (_a, positive); } /// @notice extracts the sign of a signed int /// @param a signed int /// @return _a unsigned int /// @return bool sign of the provided int function extractSign(int256 a) internal pure returns (uint256 _a, bool) { return extractSign(a, true); } /// @notice returns the max of two int256 numbers /// @param a first number /// @param b second number /// @return c = max of a and b function max(int256 a, int256 b) internal pure returns (int256 c) { if (a > b) c = a; else c = b; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; import { LiquidityPosition } from './LiquidityPosition.sol'; import { Protocol } from './Protocol.sol'; import { Uint48Lib } from './Uint48.sol'; import { Uint48L5ArrayLib } from './Uint48L5Array.sol'; import { IClearingHouseStructures } from '../interfaces/clearinghouse/IClearingHouseStructures.sol'; import { IVPoolWrapper } from '../interfaces/IVPoolWrapper.sol'; /// @title Liquidity position set functions library LiquidityPositionSet { using LiquidityPosition for LiquidityPosition.Info; using LiquidityPositionSet for LiquidityPosition.Set; using Protocol for Protocol.Info; using Uint48Lib for int24; using Uint48Lib for uint48; using Uint48L5ArrayLib for uint48[5]; error LPS_IllegalTicks(int24 tickLower, int24 tickUpper); error LPS_DeactivationFailed(int24 tickLower, int24 tickUpper, uint256 liquidity); error LPS_InactiveRange(); /// @notice denotes token position change due to liquidity add/remove /// @param accountId serial number of the account /// @param poolId address of token whose position was taken /// @param tickLower lower tick of the range updated /// @param tickUpper upper tick of the range updated /// @param vTokenAmountOut amount of tokens that account received (positive) or paid (negative) event TokenPositionChangedDueToLiquidityChanged( uint256 indexed accountId, uint32 indexed poolId, int24 tickLower, int24 tickUpper, int256 vTokenAmountOut ); /** * Internal methods */ /// @notice activates a position by initializing it and adding it to the set /// @param set storage ref to the account's set of liquidity positions of a pool /// @param tickLower lower tick of the range to be activated /// @param tickUpper upper tick of the range to be activated /// @return position storage ref of the activated position function activate( LiquidityPosition.Set storage set, int24 tickLower, int24 tickUpper ) internal returns (LiquidityPosition.Info storage position) { if (tickLower > tickUpper) { revert LPS_IllegalTicks(tickLower, tickUpper); } uint48 positionId; set.active.include(positionId = tickLower.concat(tickUpper)); position = set.positions[positionId]; if (!position.isInitialized()) { position.initialize(tickLower, tickUpper); } } /// @notice deactivates a position by removing it from the set /// @param set storage ref to the account's set of liquidity positions of a pool /// @param position storage ref to the position to be deactivated function deactivate(LiquidityPosition.Set storage set, LiquidityPosition.Info storage position) internal { if (position.liquidity != 0) { revert LPS_DeactivationFailed(position.tickLower, position.tickUpper, position.liquidity); } set.active.exclude(position.tickLower.concat(position.tickUpper)); } /// @notice changes liquidity of a position in the set /// @param set storage ref to the account's set of liquidity positions of a pool /// @param accountId serial number of the account /// @param poolId truncated address of vToken /// @param liquidityChangeParams parameters of the liquidity change /// @param balanceAdjustments adjustments to made to the account's balance later /// @param protocol ref to the state of the protocol function liquidityChange( LiquidityPosition.Set storage set, uint256 accountId, uint32 poolId, IClearingHouseStructures.LiquidityChangeParams memory liquidityChangeParams, IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments, Protocol.Info storage protocol ) internal { LiquidityPosition.Info storage position = set.activate( liquidityChangeParams.tickLower, liquidityChangeParams.tickUpper ); position.limitOrderType = liquidityChangeParams.limitOrderType; set.liquidityChange( accountId, poolId, position, liquidityChangeParams.liquidityDelta, balanceAdjustments, protocol ); } /// @notice changes liquidity of a position in the set /// @param accountId serial number of the account /// @param poolId truncated address of vToken /// @param position storage ref to the position to be changed /// @param liquidityDelta amount of liquidity to be added or removed /// @param balanceAdjustments adjustments to made to the account's balance later /// @param protocol ref to the state of the protocol function liquidityChange( LiquidityPosition.Set storage set, uint256 accountId, uint32 poolId, LiquidityPosition.Info storage position, int128 liquidityDelta, IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments, Protocol.Info storage protocol ) internal { position.liquidityChange(accountId, poolId, liquidityDelta, balanceAdjustments, protocol); emit TokenPositionChangedDueToLiquidityChanged( accountId, poolId, position.tickLower, position.tickUpper, balanceAdjustments.vTokenIncrease ); if (position.liquidity == 0) { set.deactivate(position); } } /// @notice removes liquidity from a position in the set /// @param set storage ref to the account's set of liquidity positions of a pool /// @param accountId serial number of the account /// @param poolId truncated address of vToken /// @param position storage ref to the position to be closed /// @param balanceAdjustments adjustments to made to the account's balance later /// @param protocol ref to the state of the protocol function closeLiquidityPosition( LiquidityPosition.Set storage set, uint256 accountId, uint32 poolId, LiquidityPosition.Info storage position, IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments, Protocol.Info storage protocol ) internal { set.liquidityChange(accountId, poolId, position, -int128(position.liquidity), balanceAdjustments, protocol); } /// @notice removes liquidity from a position in the set /// @param set storage ref to the account's set of liquidity positions of a pool /// @param accountId serial number of the account /// @param poolId truncated address of vToken /// @param currentTick current tick of the pool /// @param tickLower lower tick of the range to be closed /// @param tickUpper upper tick of the range to be closed /// @param balanceAdjustments adjustments to made to the account's balance later /// @param protocol ref to the state of the protocol function removeLimitOrder( LiquidityPosition.Set storage set, uint256 accountId, uint32 poolId, int24 currentTick, int24 tickLower, int24 tickUpper, IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments, Protocol.Info storage protocol ) internal { LiquidityPosition.Info storage position = set.getLiquidityPosition(tickLower, tickUpper); position.checkValidLimitOrderRemoval(currentTick); set.closeLiquidityPosition(accountId, poolId, position, balanceAdjustments, protocol); } /// @notice removes liquidity from all the positions in the set /// @param set storage ref to the account's set of liquidity positions of a pool /// @param accountId serial number of the account /// @param poolId truncated address of vToken /// @param balanceAdjustments adjustments to made to the account's balance later /// @param protocol ref to the state of the protocol function closeAllLiquidityPositions( LiquidityPosition.Set storage set, uint256 accountId, uint32 poolId, IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments, Protocol.Info storage protocol ) internal { LiquidityPosition.Info storage position; while (set.active[0] != 0) { IClearingHouseStructures.BalanceAdjustments memory balanceAdjustmentsCurrent; position = set.positions[set.active[0]]; set.closeLiquidityPosition(accountId, poolId, position, balanceAdjustmentsCurrent, protocol); balanceAdjustments.vQuoteIncrease += balanceAdjustmentsCurrent.vQuoteIncrease; balanceAdjustments.vTokenIncrease += balanceAdjustmentsCurrent.vTokenIncrease; balanceAdjustments.traderPositionIncrease += balanceAdjustmentsCurrent.traderPositionIncrease; } } /** * Internal view methods */ /// @notice gets the liquidity position of a tick range /// @param set storage ref to the account's set of liquidity positions of a pool /// @param tickLower lower tick of the range to be closed /// @param tickUpper upper tick of the range to be closed /// @return position liquidity position of the tick range function getLiquidityPosition( LiquidityPosition.Set storage set, int24 tickLower, int24 tickUpper ) internal view returns (LiquidityPosition.Info storage position) { if (tickLower > tickUpper) { revert LPS_IllegalTicks(tickLower, tickUpper); } uint48 positionId = Uint48Lib.concat(tickLower, tickUpper); position = set.positions[positionId]; if (!position.isInitialized()) revert LPS_InactiveRange(); return position; } /// @notice gets information about all the liquidity position /// @param set storage ref to the account's set of liquidity positions of a pool /// @return liquidityPositions Information about all the liquidity position for the pool function getInfo(LiquidityPosition.Set storage set) internal view returns (IClearingHouseStructures.LiquidityPositionView[] memory liquidityPositions) { uint256 numberOfTokenPositions = set.active.numberOfNonZeroElements(); liquidityPositions = new IClearingHouseStructures.LiquidityPositionView[](numberOfTokenPositions); for (uint256 i = 0; i < numberOfTokenPositions; i++) { liquidityPositions[i].limitOrderType = set.positions[set.active[i]].limitOrderType; liquidityPositions[i].tickLower = set.positions[set.active[i]].tickLower; liquidityPositions[i].tickUpper = set.positions[set.active[i]].tickUpper; liquidityPositions[i].liquidity = set.positions[set.active[i]].liquidity; liquidityPositions[i].vTokenAmountIn = set.positions[set.active[i]].vTokenAmountIn; liquidityPositions[i].sumALastX128 = set.positions[set.active[i]].sumALastX128; liquidityPositions[i].sumBInsideLastX128 = set.positions[set.active[i]].sumBInsideLastX128; liquidityPositions[i].sumFpInsideLastX128 = set.positions[set.active[i]].sumFpInsideLastX128; liquidityPositions[i].sumFeeInsideLastX128 = set.positions[set.active[i]].sumFeeInsideLastX128; } } /// @notice gets the net position due to all the liquidity positions /// @param set storage ref to the account's set of liquidity positions of a pool /// @param sqrtPriceCurrent current sqrt price of the pool /// @return netPosition due to all the liquidity positions function getNetPosition(LiquidityPosition.Set storage set, uint160 sqrtPriceCurrent) internal view returns (int256 netPosition) { uint256 numberOfTokenPositions = set.active.numberOfNonZeroElements(); for (uint256 i = 0; i < numberOfTokenPositions; i++) { netPosition += set.positions[set.active[i]].netPosition(sqrtPriceCurrent); } } /// @notice checks whether the liquidity position set is empty /// @param set storage ref to the account's set of liquidity positions of a pool /// @return true if the liquidity position set is empty function isEmpty(LiquidityPosition.Set storage set) internal view returns (bool) { return set.active.isEmpty(); } /// @notice checks whether for given ticks, a liquidity position is active /// @param set storage ref to the account's set of liquidity positions of a pool /// @param tickLower lower tick of the range /// @param tickUpper upper tick of the range /// @return true if the liquidity position is active function isPositionActive( LiquidityPosition.Set storage set, int24 tickLower, int24 tickUpper ) internal view returns (bool) { return set.active.exists(tickLower.concat(tickUpper)); } /// @notice gets the total long side risk for all the active liquidity positions /// @param set storage ref to the account's set of liquidity positions of a pool /// @param valuationPriceX96 price used to value the vToken asset /// @return risk the net long side risk for all the active liquidity positions function longSideRisk(LiquidityPosition.Set storage set, uint160 valuationPriceX96) internal view returns (uint256 risk) { for (uint256 i = 0; i < set.active.length; i++) { uint48 id = set.active[i]; if (id == 0) break; risk += set.positions[id].longSideRisk(valuationPriceX96); } } /// @notice gets the total market value of all the active liquidity positions /// @param set storage ref to the account's set of liquidity positions of a pool /// @param sqrtPriceCurrent price used to value the vToken asset /// @param poolId the id of the pool /// @param protocol ref to the state of the protocol /// @return marketValue_ the total market value of all the active liquidity positions function marketValue( LiquidityPosition.Set storage set, uint160 sqrtPriceCurrent, uint32 poolId, Protocol.Info storage protocol ) internal view returns (int256 marketValue_) { marketValue_ = set.marketValue(sqrtPriceCurrent, protocol.vPoolWrapper(poolId)); } /// @notice Get the total market value of all active liquidity positions in the set. /// @param set: Collection of active liquidity positions /// @param sqrtPriceCurrent: Current price of the virtual asset /// @param wrapper: address of the wrapper contract, passed once to avoid multiple sloads for wrapper function marketValue( LiquidityPosition.Set storage set, uint160 sqrtPriceCurrent, IVPoolWrapper wrapper ) internal view returns (int256 marketValue_) { for (uint256 i = 0; i < set.active.length; i++) { uint48 id = set.active[i]; if (id == 0) break; marketValue_ += set.positions[id].marketValue(sqrtPriceCurrent, wrapper); } } /// @notice gets the max net position possible due to all the liquidity positions /// @param set storage ref to the account's set of liquidity positions of a pool /// @return risk the max net position possible due to all the liquidity positions function maxNetPosition(LiquidityPosition.Set storage set) internal view returns (uint256 risk) { for (uint256 i = 0; i < set.active.length; i++) { uint48 id = set.active[i]; if (id == 0) break; risk += set.positions[id].maxNetPosition(); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; import { SqrtPriceMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/SqrtPriceMath.sol'; import { TickMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/TickMath.sol'; import { SafeCast } from '@uniswap/v3-core-0.8-support/contracts/libraries/SafeCast.sol'; import { FixedPoint96 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint96.sol'; import { FixedPoint128 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint128.sol'; import { FullMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/FullMath.sol'; import { IUniswapV3Pool } from '@uniswap/v3-core-0.8-support/contracts/interfaces/IUniswapV3Pool.sol'; import { PriceMath } from './PriceMath.sol'; import { Protocol } from './Protocol.sol'; import { SignedFullMath } from './SignedFullMath.sol'; import { UniswapV3PoolHelper } from './UniswapV3PoolHelper.sol'; import { FundingPayment } from './FundingPayment.sol'; import { IClearingHouseStructures } from '../interfaces/clearinghouse/IClearingHouseStructures.sol'; import { IClearingHouseEnums } from '../interfaces/clearinghouse/IClearingHouseEnums.sol'; import { IVPoolWrapper } from '../interfaces/IVPoolWrapper.sol'; /// @title Liquidity position functions library LiquidityPosition { using FullMath for uint256; using PriceMath for uint160; using SafeCast for uint256; using SignedFullMath for int256; using UniswapV3PoolHelper for IUniswapV3Pool; using LiquidityPosition for LiquidityPosition.Info; using Protocol for Protocol.Info; struct Set { // multiple per pool because it's non-fungible, allows for 4 billion LP positions lifetime uint48[5] active; // concat(tickLow,tickHigh) mapping(uint48 => LiquidityPosition.Info) positions; uint256[100] _emptySlots; // reserved for adding variables when upgrading logic } struct Info { //Extra boolean to check if it is limit order and uint to track limit price. IClearingHouseEnums.LimitOrderType limitOrderType; // the tick range of the position; int24 tickLower; int24 tickUpper; // the liquidity of the position uint128 liquidity; int256 vTokenAmountIn; // funding payment checkpoints int256 sumALastX128; int256 sumBInsideLastX128; int256 sumFpInsideLastX128; // fee growth inside uint256 sumFeeInsideLastX128; uint256[100] _emptySlots; // reserved for adding variables when upgrading logic } error LP_AlreadyInitialized(); error LP_IneligibleLimitOrderRemoval(); /// @notice denotes liquidity add/remove /// @param accountId serial number of the account /// @param poolId address of token whose position was taken /// @param tickLower lower tick of the range updated /// @param tickUpper upper tick of the range updated /// @param liquidityDelta change in liquidity value /// @param limitOrderType the type of range position /// @param vTokenAmountOut amount of tokens that account received (positive) or paid (negative) /// @param vQuoteAmountOut amount of vQuote tokens that account received (positive) or paid (negative) event LiquidityChanged( uint256 indexed accountId, uint32 indexed poolId, int24 tickLower, int24 tickUpper, int128 liquidityDelta, IClearingHouseEnums.LimitOrderType limitOrderType, int256 vTokenAmountOut, int256 vQuoteAmountOut, uint160 sqrtPriceX96 ); /// @param accountId serial number of the account /// @param poolId address of token for which funding was paid /// @param tickLower lower tick of the range for which funding was paid /// @param tickUpper upper tick of the range for which funding was paid /// @param amount amount of funding paid (negative) or received (positive) /// @param sumALastX128 val of sum of the term A in funding payment math, when op took place /// @param sumBInsideLastX128 val of sum of the term B in funding payment math, when op took place /// @param sumFpInsideLastX128 val of sum of the term Fp in funding payment math, when op took place /// @param sumFeeInsideLastX128 val of sum of the term Fee in wrapper, when op took place event LiquidityPositionFundingPaymentRealized( uint256 indexed accountId, uint32 indexed poolId, int24 tickLower, int24 tickUpper, int256 amount, int256 sumALastX128, int256 sumBInsideLastX128, int256 sumFpInsideLastX128, uint256 sumFeeInsideLastX128 ); /// @notice denotes fee payment for a range / token position /// @dev for a token position tickLower = tickUpper = 0 /// @param accountId serial number of the account /// @param poolId address of token for which fee was paid /// @param tickLower lower tick of the range for which fee was paid /// @param tickUpper upper tick of the range for which fee was paid /// @param amount amount of fee paid (negative) or received (positive) event LiquidityPositionEarningsRealized( uint256 indexed accountId, uint32 indexed poolId, int24 tickLower, int24 tickUpper, int256 amount ); /** * Internal methods */ /// @notice initializes a new LiquidityPosition.Info struct /// @dev Reverts if the position is already initialized /// @param position storage pointer of the position to initialize /// @param tickLower lower tick of the range /// @param tickUpper upper tick of the range function initialize( LiquidityPosition.Info storage position, int24 tickLower, int24 tickUpper ) internal { if (position.isInitialized()) { revert LP_AlreadyInitialized(); } position.tickLower = tickLower; position.tickUpper = tickUpper; } /// @notice changes liquidity for a position, informs pool wrapper and does necessary bookkeeping /// @param position storage ref of the position to update /// @param accountId serial number of the account, used to emit event /// @param poolId id of the pool for which position was updated /// @param liquidityDelta change in liquidity value /// @param balanceAdjustments memory ref to the balance adjustments struct /// @param protocol ref to the protocol state function liquidityChange( LiquidityPosition.Info storage position, uint256 accountId, uint32 poolId, int128 liquidityDelta, IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments, Protocol.Info storage protocol ) internal { int256 vTokenPrincipal; int256 vQuotePrincipal; IVPoolWrapper wrapper = protocol.vPoolWrapper(poolId); IVPoolWrapper.WrapperValuesInside memory wrapperValuesInside; // calls wrapper to mint/burn liquidity if (liquidityDelta > 0) { uint256 vTokenPrincipal_; uint256 vQuotePrincipal_; (vTokenPrincipal_, vQuotePrincipal_, wrapperValuesInside) = wrapper.mint( position.tickLower, position.tickUpper, uint128(liquidityDelta) ); vTokenPrincipal = vTokenPrincipal_.toInt256(); vQuotePrincipal = vQuotePrincipal_.toInt256(); } else { uint256 vTokenPrincipal_; uint256 vQuotePrincipal_; (vTokenPrincipal_, vQuotePrincipal_, wrapperValuesInside) = wrapper.burn( position.tickLower, position.tickUpper, uint128(-liquidityDelta) ); vTokenPrincipal = -vTokenPrincipal_.toInt256(); vQuotePrincipal = -vQuotePrincipal_.toInt256(); } // calculate funding payment and liquidity fees then update checkpoints position.update(accountId, poolId, wrapperValuesInside, balanceAdjustments); // adjust in the token acounts balanceAdjustments.vQuoteIncrease -= vQuotePrincipal; balanceAdjustments.vTokenIncrease -= vTokenPrincipal; // emit the event uint160 sqrtPriceCurrent = protocol.vPool(poolId).sqrtPriceCurrent(); emitLiquidityChangeEvent( position, accountId, poolId, liquidityDelta, sqrtPriceCurrent, -vTokenPrincipal, -vQuotePrincipal ); // update trader position increase int256 vTokenAmountCurrent; { (vTokenAmountCurrent, ) = position.vTokenAmountsInRange(sqrtPriceCurrent, false); balanceAdjustments.traderPositionIncrease += (vTokenAmountCurrent - position.vTokenAmountIn); } uint128 liquidityNew = position.liquidity; if (liquidityDelta > 0) { liquidityNew += uint128(liquidityDelta); } else if (liquidityDelta < 0) { liquidityNew -= uint128(-liquidityDelta); } if (liquidityNew != 0) { // update state position.liquidity = liquidityNew; position.vTokenAmountIn = vTokenAmountCurrent + vTokenPrincipal; } else { // clear all the state position.liquidity = 0; position.vTokenAmountIn = 0; position.sumALastX128 = 0; position.sumBInsideLastX128 = 0; position.sumFpInsideLastX128 = 0; position.sumFeeInsideLastX128 = 0; } } /// @notice updates the position with latest checkpoints, and realises fees and fp /// @dev fees and funding payment are not immediately adjusted in token balance state, /// balanceAdjustments struct is used to pass the necessary values to caller. /// @param position storage ref of the position to update /// @param accountId serial number of the account, used to emit event /// @param poolId id of the pool for which position was updated /// @param wrapperValuesInside range checkpoint values from the wrapper /// @param balanceAdjustments memory ref to the balance adjustments struct function update( LiquidityPosition.Info storage position, uint256 accountId, uint32 poolId, IVPoolWrapper.WrapperValuesInside memory wrapperValuesInside, IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments ) internal { int256 fundingPayment = position.unrealizedFundingPayment( wrapperValuesInside.sumAX128, wrapperValuesInside.sumFpInsideX128 ); balanceAdjustments.vQuoteIncrease += fundingPayment; int256 unrealizedLiquidityFee = position.unrealizedFees(wrapperValuesInside.sumFeeInsideX128).toInt256(); balanceAdjustments.vQuoteIncrease += unrealizedLiquidityFee; // updating checkpoints position.sumALastX128 = wrapperValuesInside.sumAX128; position.sumBInsideLastX128 = wrapperValuesInside.sumBInsideX128; position.sumFpInsideLastX128 = wrapperValuesInside.sumFpInsideX128; position.sumFeeInsideLastX128 = wrapperValuesInside.sumFeeInsideX128; emit LiquidityPositionFundingPaymentRealized( accountId, poolId, position.tickLower, position.tickUpper, fundingPayment, wrapperValuesInside.sumAX128, wrapperValuesInside.sumBInsideX128, wrapperValuesInside.sumFpInsideX128, wrapperValuesInside.sumFeeInsideX128 ); emit LiquidityPositionEarningsRealized( accountId, poolId, position.tickLower, position.tickUpper, unrealizedLiquidityFee ); } /** * Internal view methods */ /// @notice ensures that limit order removal is valid, else reverts /// @param info storage ref of the position to check /// @param currentTick current tick in the pool function checkValidLimitOrderRemoval(LiquidityPosition.Info storage info, int24 currentTick) internal view { if ( !((currentTick >= info.tickUpper && info.limitOrderType == IClearingHouseEnums.LimitOrderType.UPPER_LIMIT) || (currentTick <= info.tickLower && info.limitOrderType == IClearingHouseEnums.LimitOrderType.LOWER_LIMIT)) ) { revert LP_IneligibleLimitOrderRemoval(); } } /// @notice checks if the position is initialized /// @param info storage ref of the position to check /// @return true if the position is initialized function isInitialized(LiquidityPosition.Info storage info) internal view returns (bool) { return info.tickLower != 0 || info.tickUpper != 0; } /// @notice calculates the long side risk for the position /// @param position storage ref of the position to check /// @param valuationSqrtPriceX96 valuation sqrt price in x96 /// @return long side risk function longSideRisk(LiquidityPosition.Info storage position, uint160 valuationSqrtPriceX96) internal view returns (uint256) { uint160 sqrtPriceLowerX96 = TickMath.getSqrtRatioAtTick(position.tickLower); uint160 sqrtPriceUpperX96 = TickMath.getSqrtRatioAtTick(position.tickUpper); uint256 longPositionExecutionPriceX128; { uint160 sqrtPriceUpperMinX96 = valuationSqrtPriceX96 <= sqrtPriceUpperX96 ? valuationSqrtPriceX96 : sqrtPriceUpperX96; uint160 sqrtPriceLowerMinX96 = valuationSqrtPriceX96 <= sqrtPriceLowerX96 ? valuationSqrtPriceX96 : sqrtPriceLowerX96; longPositionExecutionPriceX128 = uint256(sqrtPriceLowerMinX96).mulDiv(sqrtPriceUpperMinX96, 1 << 64); } uint256 maxNetLongPosition; { uint256 maxLongTokens = SqrtPriceMath.getAmount0Delta( sqrtPriceLowerX96, sqrtPriceUpperX96, position.liquidity, true ); // if (position.vTokenAmountIn >= 0) { //maxLongTokens in range should always be >= amount that got added to range, equality occurs when range was added at pCurrent = pHigh assert(maxLongTokens >= uint256(position.vTokenAmountIn)); maxNetLongPosition = maxLongTokens - uint256(position.vTokenAmountIn); } else maxNetLongPosition = maxLongTokens + uint256(-1 * position.vTokenAmountIn); } return maxNetLongPosition.mulDiv(longPositionExecutionPriceX128, FixedPoint128.Q128); } /// @notice calculates the market value for the position using a provided price /// @param position storage ref of the position to check /// @param valuationSqrtPriceX96 valuation sqrt price to be used /// @param wrapper address of the pool wrapper /// @return marketValue_ the market value of the position function marketValue( LiquidityPosition.Info storage position, uint160 valuationSqrtPriceX96, IVPoolWrapper wrapper ) internal view returns (int256 marketValue_) { { (int256 vTokenAmount, int256 vQuoteAmount) = position.vTokenAmountsInRange(valuationSqrtPriceX96, false); uint256 priceX128 = valuationSqrtPriceX96.toPriceX128(); marketValue_ = vTokenAmount.mulDiv(priceX128, FixedPoint128.Q128) + vQuoteAmount; } // adding fees IVPoolWrapper.WrapperValuesInside memory wrapperValuesInside = wrapper.getExtrapolatedValuesInside( position.tickLower, position.tickUpper ); marketValue_ += position.unrealizedFees(wrapperValuesInside.sumFeeInsideX128).toInt256(); marketValue_ += position.unrealizedFundingPayment( wrapperValuesInside.sumAX128, wrapperValuesInside.sumFpInsideX128 ); } /// @notice calculates the max net position for the position /// @param position storage ref of the position to check /// @return maxNetPosition the max net position of the position function maxNetPosition(LiquidityPosition.Info storage position) internal view returns (uint256) { uint160 sqrtPriceLowerX96 = TickMath.getSqrtRatioAtTick(position.tickLower); uint160 sqrtPriceUpperX96 = TickMath.getSqrtRatioAtTick(position.tickUpper); if (position.vTokenAmountIn >= 0) return SqrtPriceMath.getAmount0Delta(sqrtPriceLowerX96, sqrtPriceUpperX96, position.liquidity, true) - uint256(position.vTokenAmountIn); else return SqrtPriceMath.getAmount0Delta(sqrtPriceLowerX96, sqrtPriceUpperX96, position.liquidity, true) + uint256(-1 * position.vTokenAmountIn); } /// @notice calculates the current net position for the position /// @param position storage ref of the position to check /// @param sqrtPriceCurrent the current sqrt price, used to calculate net position /// @return netTokenPosition the current net position of the position function netPosition(LiquidityPosition.Info storage position, uint160 sqrtPriceCurrent) internal view returns (int256 netTokenPosition) { int256 vTokenAmountCurrent; (vTokenAmountCurrent, ) = position.vTokenAmountsInRange(sqrtPriceCurrent, false); netTokenPosition = (vTokenAmountCurrent - position.vTokenAmountIn); } /// @notice calculates the current virtual token amounts for the position /// @param position storage ref of the position to check /// @param sqrtPriceCurrent the current sqrt price, used to calculate virtual token amounts /// @param roundUp whether to round up the token amounts, purpose to charge user more and give less /// @return vTokenAmount the current vToken amount /// @return vQuoteAmount the current vQuote amount function vTokenAmountsInRange( LiquidityPosition.Info storage position, uint160 sqrtPriceCurrent, bool roundUp ) internal view returns (int256 vTokenAmount, int256 vQuoteAmount) { uint160 sqrtPriceLowerX96 = TickMath.getSqrtRatioAtTick(position.tickLower); uint160 sqrtPriceUpperX96 = TickMath.getSqrtRatioAtTick(position.tickUpper); // If price is outside the range, then consider it at the ends // for calculation of amounts uint160 sqrtPriceMiddleX96 = sqrtPriceCurrent; if (sqrtPriceCurrent < sqrtPriceLowerX96) { sqrtPriceMiddleX96 = sqrtPriceLowerX96; } else if (sqrtPriceCurrent > sqrtPriceUpperX96) { sqrtPriceMiddleX96 = sqrtPriceUpperX96; } vTokenAmount = SqrtPriceMath .getAmount0Delta(sqrtPriceMiddleX96, sqrtPriceUpperX96, position.liquidity, roundUp) .toInt256(); vQuoteAmount = SqrtPriceMath .getAmount1Delta(sqrtPriceLowerX96, sqrtPriceMiddleX96, position.liquidity, roundUp) .toInt256(); } /// @notice returns vQuoteIncrease due to unrealised funding payment for the liquidity position (+ve means receiving and -ve means giving) /// @param position storage ref of the position to check /// @param sumAX128 the sumA value from the pool wrapper /// @param sumFpInsideX128 the sumFp in the position's range from the pool wrapper /// @return vQuoteIncrease the amount of vQuote that should be added to the account's vQuote balance function unrealizedFundingPayment( LiquidityPosition.Info storage position, int256 sumAX128, int256 sumFpInsideX128 ) internal view returns (int256 vQuoteIncrease) { // subtract the bill from the account's vQuote balance vQuoteIncrease = -FundingPayment.bill( sumAX128, sumFpInsideX128, position.sumALastX128, position.sumBInsideLastX128, position.sumFpInsideLastX128, position.liquidity ); } /// @notice calculates the unrealised lp fees for the position /// @param position storage ref of the position to check /// @param sumFeeInsideX128 the global sumFee in the position's range from the pool wrapper /// @return vQuoteIncrease the amount of vQuote that should be added to the account's vQuote balance function unrealizedFees(LiquidityPosition.Info storage position, uint256 sumFeeInsideX128) internal view returns (uint256 vQuoteIncrease) { vQuoteIncrease = (sumFeeInsideX128 - position.sumFeeInsideLastX128).mulDiv( position.liquidity, FixedPoint128.Q128 ); } function emitLiquidityChangeEvent( LiquidityPosition.Info storage position, uint256 accountId, uint32 poolId, int128 liquidityDelta, uint160 sqrtPriceX96, int256 vTokenAmountOut, int256 vQuoteAmountOut ) internal { emit LiquidityChanged( accountId, poolId, position.tickLower, position.tickUpper, liquidityDelta, position.limitOrderType, vTokenAmountOut, vQuoteAmountOut, sqrtPriceX96 ); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.4; import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import { IUniswapV3Pool } from '@uniswap/v3-core-0.8-support/contracts/interfaces/IUniswapV3Pool.sol'; import { Math } from '@openzeppelin/contracts/utils/math/Math.sol'; import { FixedPoint128 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint128.sol'; import { FullMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/FullMath.sol'; import { IClearingHouseStructures } from '../interfaces/clearinghouse/IClearingHouseStructures.sol'; import { IVQuote } from '../interfaces/IVQuote.sol'; import { IVToken } from '../interfaces/IVToken.sol'; import { IVPoolWrapper } from '../interfaces/IVPoolWrapper.sol'; import { PriceMath } from './PriceMath.sol'; import { SafeCast } from './SafeCast.sol'; import { SignedMath } from './SignedMath.sol'; import { SignedFullMath } from './SignedFullMath.sol'; import { UniswapV3PoolHelper } from './UniswapV3PoolHelper.sol'; import { Block } from './Block.sol'; import { SafeCast } from './SafeCast.sol'; /// @title Protocol storage functions /// @dev This is used as main storage interface containing protocol info library Protocol { using FullMath for uint256; using PriceMath for uint160; using PriceMath for uint256; using SignedMath for int256; using SignedFullMath for int256; using SafeCast for uint256; using UniswapV3PoolHelper for IUniswapV3Pool; using SafeCast for uint256; using Protocol for Protocol.Info; struct PriceCache { uint32 updateBlockNumber; uint224 virtualPriceX128; uint224 realPriceX128; bool isDeviationBreached; } struct Info { // poolId => PoolInfo mapping(uint32 => IClearingHouseStructures.Pool) pools; // collateralId => CollateralInfo mapping(uint32 => IClearingHouseStructures.Collateral) collaterals; // iterable and increasing list of pools (used for admin functions) uint32[] poolIds; // settlement token (default collateral) IERC20 settlementToken; // virtual quote token (sort of fake USDC), is always token1 in uniswap pools IVQuote vQuote; // accounting settings IClearingHouseStructures.LiquidationParams liquidationParams; uint256 minRequiredMargin; uint256 removeLimitOrderFee; uint256 minimumOrderNotional; // price cache mapping(uint32 => PriceCache) priceCache; // reserved for adding slots in future uint256[100] _emptySlots; } function updatePoolPriceCache(Protocol.Info storage protocol, uint32 poolId) internal { uint32 blockNumber = Block.number(); PriceCache storage poolPriceCache = protocol.priceCache[poolId]; if (poolPriceCache.updateBlockNumber == blockNumber) { return; } uint256 realPriceX128 = protocol.getRealTwapPriceX128(poolId); uint256 virtualPriceX128 = protocol.getVirtualTwapPriceX128(poolId); // In case the price is breaching the Q224 limit, we do not cache it uint256 Q224 = 1 << 224; if (realPriceX128 >= Q224 || virtualPriceX128 >= Q224) { return; } uint16 maxDeviationBps = protocol.pools[poolId].settings.maxVirtualPriceDeviationRatioBps; if ( // if virtual price is too off from real price then screw that, we'll just use real price (int256(realPriceX128) - int256(virtualPriceX128)).absUint() > realPriceX128.mulDiv(maxDeviationBps, 1e4) ) { poolPriceCache.isDeviationBreached = true; } else { poolPriceCache.isDeviationBreached = false; } poolPriceCache.realPriceX128 = realPriceX128.toUint224(); poolPriceCache.virtualPriceX128 = virtualPriceX128.toUint224(); poolPriceCache.updateBlockNumber = blockNumber; } /// @notice gets the uniswap v3 pool address for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return UniswapV3Pool contract object function vPool(Protocol.Info storage protocol, uint32 poolId) internal view returns (IUniswapV3Pool) { return protocol.pools[poolId].vPool; } /// @notice gets the wrapper address for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return VPoolWrapper contract object function vPoolWrapper(Protocol.Info storage protocol, uint32 poolId) internal view returns (IVPoolWrapper) { return protocol.pools[poolId].vPoolWrapper; } /// @notice gets the virtual twap sqrt price for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return sqrtPriceX96 virtual twap sqrt price function getVirtualTwapSqrtPriceX96(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint160 sqrtPriceX96) { IClearingHouseStructures.Pool storage pool = protocol.pools[poolId]; return pool.vPool.twapSqrtPrice(pool.settings.twapDuration); } /// @notice gets the virtual current sqrt price for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return sqrtPriceX96 virtual current sqrt price function getVirtualCurrentSqrtPriceX96(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint160 sqrtPriceX96) { return protocol.pools[poolId].vPool.sqrtPriceCurrent(); } /// @notice gets the virtual current tick for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return tick virtual current tick function getVirtualCurrentTick(Protocol.Info storage protocol, uint32 poolId) internal view returns (int24 tick) { return protocol.pools[poolId].vPool.tickCurrent(); } /// @notice gets the virtual twap price for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return priceX128 virtual twap price function getVirtualTwapPriceX128(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint256 priceX128) { return protocol.getVirtualTwapSqrtPriceX96(poolId).toPriceX128(); } /// @notice gets the virtual current price for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return priceX128 virtual current price function getVirtualCurrentPriceX128(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint256 priceX128) { return protocol.getVirtualCurrentSqrtPriceX96(poolId).toPriceX128(); } /// @notice gets the real twap price for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return priceX128 virtual twap price function getRealTwapPriceX128(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint256 priceX128) { IClearingHouseStructures.Pool storage pool = protocol.pools[poolId]; return pool.settings.oracle.getTwapPriceX128(pool.settings.twapDuration); } /// @notice gets the twap prices with deviation check for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return realPriceX128 the real price /// @return virtualPriceX128 the virtual price if under deviation else real price function getTwapPricesWithDeviationCheck(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint256 realPriceX128, uint256 virtualPriceX128) { realPriceX128 = protocol.getRealTwapPriceX128(poolId); virtualPriceX128 = protocol.getVirtualTwapPriceX128(poolId); uint16 maxDeviationBps = protocol.pools[poolId].settings.maxVirtualPriceDeviationRatioBps; uint256 priceDeltaX128 = realPriceX128 > virtualPriceX128 ? realPriceX128 - virtualPriceX128 : virtualPriceX128 - realPriceX128; if (priceDeltaX128 > realPriceX128.mulDiv(maxDeviationBps, 1e4)) { // if virtual price is too off from real price then screw that, we'll just use real price virtualPriceX128 = realPriceX128; } return (realPriceX128, virtualPriceX128); } function getCachedVirtualTwapPriceX128(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint256 priceX128) { uint32 blockNumber = Block.number(); PriceCache storage poolPriceCache = protocol.priceCache[poolId]; if (poolPriceCache.updateBlockNumber == blockNumber) { return poolPriceCache.virtualPriceX128; } else { return protocol.getVirtualTwapPriceX128(poolId); } } function getCachedTwapPricesWithDeviationCheck(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint256 realPriceX128, uint256 virtualPriceX128) { uint32 blockNumber = Block.number(); PriceCache storage poolPriceCache = protocol.priceCache[poolId]; if (poolPriceCache.updateBlockNumber == blockNumber) { if (poolPriceCache.isDeviationBreached) { return (poolPriceCache.realPriceX128, poolPriceCache.realPriceX128); } else { return (poolPriceCache.realPriceX128, poolPriceCache.virtualPriceX128); } } else { return protocol.getTwapPricesWithDeviationCheck(poolId); } } function getCachedRealTwapPriceX128(Protocol.Info storage protocol, uint32 poolId) internal view returns (uint256 priceX128) { uint32 blockNumber = Block.number(); PriceCache storage poolPriceCache = protocol.priceCache[poolId]; if (poolPriceCache.updateBlockNumber == blockNumber) { return poolPriceCache.realPriceX128; } else { return protocol.getRealTwapPriceX128(poolId); } } /// @notice gets the margin ratio for a poolId /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @param isInitialMargin whether to use initial margin or maintainance margin /// @return margin rato in bps function getMarginRatioBps( Protocol.Info storage protocol, uint32 poolId, bool isInitialMargin ) internal view returns (uint16) { if (isInitialMargin) { return protocol.pools[poolId].settings.initialMarginRatioBps; } else { return protocol.pools[poolId].settings.maintainanceMarginRatioBps; } } /// @notice checks if the pool is cross margined /// @param protocol ref to the protocol state /// @param poolId the poolId of the pool /// @return bool whether the pool is cross margined function isPoolCrossMargined(Protocol.Info storage protocol, uint32 poolId) internal view returns (bool) { return protocol.pools[poolId].settings.isCrossMargined; } /// @notice Gives notional value of the given vToken and vQuote amounts /// @param protocol platform constants /// @param poolId id of the rage trade pool /// @param vTokenAmount amount of tokens /// @param vQuoteAmount amount of base /// @return notionalValue for the given token and vQuote amounts function getNotionalValue( Protocol.Info storage protocol, uint32 poolId, int256 vTokenAmount, int256 vQuoteAmount ) internal view returns (uint256 notionalValue) { return vTokenAmount.absUint().mulDiv(protocol.getCachedVirtualTwapPriceX128(poolId), FixedPoint128.Q128) + vQuoteAmount.absUint(); } /// @notice Gives notional value of the given token amount /// @param protocol platform constants /// @param poolId id of the rage trade pool /// @param vTokenAmount amount of tokens /// @return notionalValue for the given token and vQuote amounts function getNotionalValue( Protocol.Info storage protocol, uint32 poolId, int256 vTokenAmount ) internal view returns (uint256 notionalValue) { return protocol.getNotionalValue(poolId, vTokenAmount, 0); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; import { FullMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/FullMath.sol'; import { FixedPoint128 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint128.sol'; import { IUniswapV3Pool } from '@uniswap/v3-core-0.8-support/contracts/interfaces/IUniswapV3Pool.sol'; import { FundingPayment } from './FundingPayment.sol'; import { LiquidityPosition } from './LiquidityPosition.sol'; import { LiquidityPositionSet } from './LiquidityPositionSet.sol'; import { Protocol } from './Protocol.sol'; import { SignedFullMath } from './SignedFullMath.sol'; import { UniswapV3PoolHelper } from './UniswapV3PoolHelper.sol'; import { IVPoolWrapper } from '../interfaces/IVPoolWrapper.sol'; /// @title VToken position functions library VTokenPosition { using FullMath for uint256; using SignedFullMath for int256; using UniswapV3PoolHelper for IUniswapV3Pool; using LiquidityPosition for LiquidityPosition.Info; using LiquidityPositionSet for LiquidityPosition.Set; using Protocol for Protocol.Info; enum RISK_SIDE { LONG, SHORT } struct Set { // Fixed length array of poolId = vTokenAddress.truncate() // Open positions in 8 different pairs at same time. // Collision between poolId is not possible. uint32[8] active; // array of poolIds mapping(uint32 => VTokenPosition.Info) positions; // poolId => Position int256 vQuoteBalance; uint256[100] _emptySlots; // reserved for adding variables when upgrading logic } struct Info { int256 balance; // vTokenLong - vTokenShort int256 netTraderPosition; int256 sumALastX128; // this is moved from accounts to here because of the in margin available check // the loop needs to be done over liquidity positions of same token only LiquidityPosition.Set liquidityPositions; uint256[100] _emptySlots; // reserved for adding variables when upgrading logic } /// @notice Gives the market value of the supplied token position /// @param position token position /// @param priceX128 price in Q128 /// @param wrapper pool wrapper corresponding to position /// @return value market value with 6 decimals function marketValue( VTokenPosition.Info storage position, uint256 priceX128, IVPoolWrapper wrapper ) internal view returns (int256 value) { value = position.balance.mulDiv(priceX128, FixedPoint128.Q128); value += unrealizedFundingPayment(position, wrapper); } /// @notice returns the market value of the supplied token position /// @param position token position /// @param priceX128 price in Q128 /// @param poolId id of the rage trade pool /// @param protocol ref to the protocol state function marketValue( VTokenPosition.Info storage position, uint32 poolId, uint256 priceX128, Protocol.Info storage protocol ) internal view returns (int256 value) { return marketValue(position, priceX128, protocol.vPoolWrapper(poolId)); } /// @notice returns the market value of the supplied token position /// @param position token position /// @param poolId id of the rage trade pool /// @param protocol ref to the protocol state function marketValue( VTokenPosition.Info storage position, uint32 poolId, Protocol.Info storage protocol ) internal view returns (int256) { uint256 priceX128 = protocol.getCachedVirtualTwapPriceX128(poolId); return marketValue(position, poolId, priceX128, protocol); } function riskSide(VTokenPosition.Info storage position) internal view returns (RISK_SIDE) { return position.balance > 0 ? RISK_SIDE.LONG : RISK_SIDE.SHORT; } /// @notice returns the vQuoteIncrease due to unrealized funding payment for the trader position (+ve means receiving and -ve means paying) /// @param position token position /// @param wrapper pool wrapper corresponding to position /// @return unrealizedFpBill funding to be realized (+ve means receive and -ve means pay) function unrealizedFundingPayment(VTokenPosition.Info storage position, IVPoolWrapper wrapper) internal view returns (int256) { int256 extrapolatedSumAX128 = wrapper.getExtrapolatedSumAX128(); int256 vQuoteIncrease = -FundingPayment.bill( extrapolatedSumAX128, position.sumALastX128, position.netTraderPosition ); return vQuoteIncrease; } /// @notice gets the account's net position for a given poolId /// @param position token position /// @param poolId id of the rage trade pool /// @param protocol ref to the protocol state /// @return net position function getNetPosition( VTokenPosition.Info storage position, uint32 poolId, Protocol.Info storage protocol ) internal view returns (int256) { return position.netTraderPosition + position.liquidityPositions.getNetPosition(protocol.vPool(poolId).sqrtPriceCurrent()); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; import { FixedPoint128 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint128.sol'; import { SafeCast } from '@uniswap/v3-core-0.8-support/contracts/libraries/SafeCast.sol'; import { FullMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/FullMath.sol'; import { AddressHelper } from './AddressHelper.sol'; import { LiquidityPosition } from './LiquidityPosition.sol'; import { LiquidityPositionSet } from './LiquidityPositionSet.sol'; import { Protocol } from './Protocol.sol'; import { PriceMath } from './PriceMath.sol'; import { SignedFullMath } from './SignedFullMath.sol'; import { SignedMath } from './SignedMath.sol'; import { VTokenPosition } from './VTokenPosition.sol'; import { Uint32L8ArrayLib } from './Uint32L8Array.sol'; import { IClearingHouseStructures } from '../interfaces/clearinghouse/IClearingHouseStructures.sol'; import { IVPoolWrapper } from '../interfaces/IVPoolWrapper.sol'; import { IVToken } from '../interfaces/IVToken.sol'; /// @title VToken position set functions library VTokenPositionSet { using AddressHelper for address; using FullMath for uint256; using PriceMath for uint256; using SafeCast for uint256; using SignedFullMath for int256; using SignedMath for int256; using Uint32L8ArrayLib for uint32[8]; using LiquidityPositionSet for LiquidityPosition.Set; using Protocol for Protocol.Info; using VTokenPosition for VTokenPosition.Info; using VTokenPositionSet for VTokenPosition.Set; error VPS_IncorrectUpdate(); error VPS_DeactivationFailed(uint32 poolId); error VPS_TokenInactive(uint32 poolId); /// @notice denotes token position change /// @param accountId serial number of the account /// @param poolId truncated address of vtoken whose position was taken /// @param vTokenAmountOut amount of tokens that account received (positive) or paid (negative) /// @param vQuoteAmountOut amount of vQuote tokens that account received (positive) or paid (negative) /// @param sqrtPriceX96Start shows the sqrtPriceX96 at the start of trade execution, can be 0 if not on v3Pool /// @param sqrtPriceX96End shows the sqrtPriceX96 at the end of trade execution, can be 0 if not on v3Pool event TokenPositionChanged( uint256 indexed accountId, uint32 indexed poolId, int256 vTokenAmountOut, int256 vQuoteAmountOut, uint160 sqrtPriceX96Start, uint160 sqrtPriceX96End ); /// @notice denotes funding payment for a range / token position /// @param accountId serial number of the account /// @param poolId address of token for which funding was paid /// @param amount amount of funding paid (negative) or received (positive) /// @param sumALastX128 val of sum of the term A in funding payment math, when op took place event TokenPositionFundingPaymentRealized( uint256 indexed accountId, uint32 indexed poolId, int256 amount, int256 sumALastX128 ); /** * Internal methods */ /// @notice activates token with address 'vToken' if not already active /// @param set VTokenPositionSet /// @param poolId id of the rage trade pool function activate(VTokenPosition.Set storage set, uint32 poolId) internal { set.active.include(poolId); } /// @notice deactivates token with address 'vToken' /// @dev ensures that the balance is 0 and there are not range positions active otherwise throws an error /// @param set VTokenPositionSet /// @param poolId id of the rage trade pool function deactivate(VTokenPosition.Set storage set, uint32 poolId) internal { if (set.positions[poolId].balance != 0 || !set.positions[poolId].liquidityPositions.isEmpty()) { revert VPS_DeactivationFailed(poolId); } set.active.exclude(poolId); } /// @notice updates token balance, net trader position and vQuote balance /// @dev realizes funding payment to vQuote balance /// @dev activates the token if not already active /// @dev deactivates the token if the balance = 0 and there are no range positions active /// @dev IMP: ensure that the global states are updated using zeroSwap or directly through some interaction with pool wrapper /// @param set VTokenPositionSet /// @param balanceAdjustments platform constants /// @param poolId id of the rage trade pool /// @param accountId account identifier, used for emitting event /// @param protocol platform constants function update( VTokenPosition.Set storage set, uint256 accountId, IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments, uint32 poolId, Protocol.Info storage protocol ) internal { set.realizeFundingPayment(accountId, poolId, protocol); set.active.include(poolId); VTokenPosition.Info storage _VTokenPosition = set.positions[poolId]; _VTokenPosition.balance += balanceAdjustments.vTokenIncrease; _VTokenPosition.netTraderPosition += balanceAdjustments.traderPositionIncrease; set.vQuoteBalance += balanceAdjustments.vQuoteIncrease; if (_VTokenPosition.balance == 0 && _VTokenPosition.liquidityPositions.active[0] == 0) { set.deactivate(poolId); } } /// @notice realizes funding payment to vQuote balance /// @param set VTokenPositionSet /// @param poolId id of the rage trade pool /// @param accountId account identifier, used for emitting event /// @param protocol platform constants function realizeFundingPayment( VTokenPosition.Set storage set, uint256 accountId, uint32 poolId, Protocol.Info storage protocol ) internal { set.realizeFundingPayment(accountId, poolId, protocol.pools[poolId].vPoolWrapper); } /// @notice realizes funding payment to vQuote balance /// @param set VTokenPositionSet /// @param poolId id of the rage trade pool /// @param accountId account identifier, used for emitting event /// @param wrapper VPoolWrapper to override the set wrapper function realizeFundingPayment( VTokenPosition.Set storage set, uint256 accountId, uint32 poolId, IVPoolWrapper wrapper ) internal { VTokenPosition.Info storage position = set.positions[poolId]; int256 extrapolatedSumAX128 = wrapper.getSumAX128(); int256 fundingPayment = position.unrealizedFundingPayment(wrapper); set.vQuoteBalance += fundingPayment; position.sumALastX128 = extrapolatedSumAX128; emit TokenPositionFundingPaymentRealized(accountId, poolId, fundingPayment, extrapolatedSumAX128); } /// @notice swaps tokens (Long and Short) with input in token amount / vQuote amount /// @param set VTokenPositionSet /// @param accountId account identifier, used for emitting event /// @param poolId id of the rage trade pool /// @param swapParams parameters for swap /// @param protocol platform constants /// @return vTokenAmountOut - token amount coming out of pool /// @return vQuoteAmountOut - vQuote amount coming out of pool function swapToken( VTokenPosition.Set storage set, uint256 accountId, uint32 poolId, IClearingHouseStructures.SwapParams memory swapParams, Protocol.Info storage protocol ) internal returns (int256 vTokenAmountOut, int256 vQuoteAmountOut) { return set.swapToken(accountId, poolId, swapParams, protocol.vPoolWrapper(poolId), protocol); } /// @notice swaps tokens (Long and Short) with input in token amount /// @dev activates inactive vToe /// @param set VTokenPositionSet /// @param accountId account identifier, used for emitting event /// @param poolId id of the rage trade pool /// @param vTokenAmount amount of the token /// @param protocol platform constants /// @return vTokenAmountOut - token amount coming out of pool /// @return vQuoteAmountOut - vQuote amount coming out of pool function swapTokenAmount( VTokenPosition.Set storage set, uint256 accountId, uint32 poolId, int256 vTokenAmount, Protocol.Info storage protocol ) internal returns (int256 vTokenAmountOut, int256 vQuoteAmountOut) { return set.swapToken( accountId, poolId, /// @dev 0 means no price limit and false means amount mentioned is token amount IClearingHouseStructures.SwapParams({ amount: vTokenAmount, sqrtPriceLimit: 0, isNotional: false, isPartialAllowed: false, settleProfit: false }), protocol.vPoolWrapper(poolId), protocol ); } /// @notice swaps tokens (Long and Short) with input in token amount / vQuote amount /// @param set VTokenPositionSet /// @param accountId account identifier, used for emitting event /// @param poolId id of the rage trade pool /// @param swapParams parameters for swap /// @param wrapper VPoolWrapper to override the set wrapper /// @param protocol platform constants /// @return vTokenAmountOut - token amount coming out of pool /// @return vQuoteAmountOut - vQuote amount coming out of pool function swapToken( VTokenPosition.Set storage set, uint256 accountId, uint32 poolId, IClearingHouseStructures.SwapParams memory swapParams, IVPoolWrapper wrapper, Protocol.Info storage protocol ) internal returns (int256 vTokenAmountOut, int256 vQuoteAmountOut) { IVPoolWrapper.SwapResult memory swapResult = wrapper.swap( swapParams.amount < 0, swapParams.isNotional ? swapParams.amount : -swapParams.amount, swapParams.sqrtPriceLimit ); // change direction basis uniswap to balance increase vTokenAmountOut = -swapResult.vTokenIn; vQuoteAmountOut = -swapResult.vQuoteIn; IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments = IClearingHouseStructures .BalanceAdjustments(vQuoteAmountOut, vTokenAmountOut, vTokenAmountOut); set.update(accountId, balanceAdjustments, poolId, protocol); emit TokenPositionChanged( accountId, poolId, vTokenAmountOut, vQuoteAmountOut, swapResult.sqrtPriceX96Start, swapResult.sqrtPriceX96End ); } /// @notice function to liquidate all liquidity positions /// @param set VTokenPositionSet /// @param accountId account identifier, used for emitting event /// @param protocol platform constants /// @return notionalAmountClosed - value of net token position coming out (in notional) of all the ranges closed function liquidateLiquidityPositions( VTokenPosition.Set storage set, uint256 accountId, Protocol.Info storage protocol ) internal returns (uint256 notionalAmountClosed) { for (uint8 i = 0; i < set.active.length; i++) { uint32 truncated = set.active[i]; if (truncated == 0) break; notionalAmountClosed += set.liquidateLiquidityPositions(accountId, set.active[i], protocol); } } /// @notice function to liquidate liquidity positions for a particular token /// @param set VTokenPositionSet /// @param accountId account identifier, used for emitting event /// @param poolId id of the rage trade pool /// @param protocol platform constants /// @return notionalAmountClosed - value of net token position coming out (in notional) of all the ranges closed function liquidateLiquidityPositions( VTokenPosition.Set storage set, uint256 accountId, uint32 poolId, Protocol.Info storage protocol ) internal returns (uint256 notionalAmountClosed) { IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments; set.getTokenPosition(poolId, false).liquidityPositions.closeAllLiquidityPositions( accountId, poolId, balanceAdjustments, protocol ); set.update(accountId, balanceAdjustments, poolId, protocol); // returns notional value of token position closed return protocol.getNotionalValue(poolId, balanceAdjustments.traderPositionIncrease); } /// @notice function for liquidity add/remove /// @param set VTokenPositionSet /// @param accountId account identifier, used for emitting event /// @param poolId id of the rage trade pool /// @param liquidityChangeParams includes tickLower, tickUpper, liquidityDelta, limitOrderType /// @return vTokenAmountOut amount of tokens that account received (positive) or paid (negative) /// @return vQuoteAmountOut amount of vQuote tokens that account received (positive) or paid (negative) function liquidityChange( VTokenPosition.Set storage set, uint256 accountId, uint32 poolId, IClearingHouseStructures.LiquidityChangeParams memory liquidityChangeParams, Protocol.Info storage protocol ) internal returns (int256 vTokenAmountOut, int256 vQuoteAmountOut) { VTokenPosition.Info storage vTokenPosition = set.getTokenPosition(poolId, true); IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments; vTokenPosition.liquidityPositions.liquidityChange( accountId, poolId, liquidityChangeParams, balanceAdjustments, protocol ); set.update(accountId, balanceAdjustments, poolId, protocol); if (liquidityChangeParams.closeTokenPosition && balanceAdjustments.traderPositionIncrease != 0) { set.swapTokenAmount(accountId, poolId, -balanceAdjustments.traderPositionIncrease, protocol); } return (balanceAdjustments.vTokenIncrease, balanceAdjustments.vQuoteIncrease); } /// @notice function to remove an eligible limit order /// @dev checks whether the current price is on the correct side of the range based on the type of limit order (None, Low, High) /// @param set VTokenPositionSet /// @param accountId account identifier, used for emitting event /// @param poolId id of the rage trade pool /// @param tickLower lower tick index for the range /// @param tickUpper upper tick index for the range /// @param protocol platform constants function removeLimitOrder( VTokenPosition.Set storage set, uint256 accountId, uint32 poolId, int24 tickLower, int24 tickUpper, Protocol.Info storage protocol ) internal { VTokenPosition.Info storage vTokenPosition = set.getTokenPosition(poolId, false); IClearingHouseStructures.BalanceAdjustments memory balanceAdjustments; int24 currentTick = protocol.getVirtualCurrentTick(poolId); vTokenPosition.liquidityPositions.removeLimitOrder( accountId, poolId, currentTick, tickLower, tickUpper, balanceAdjustments, protocol ); set.update(accountId, balanceAdjustments, poolId, protocol); } function updateOpenPoolPrices(VTokenPosition.Set storage set, Protocol.Info storage protocol) internal { for (uint8 i = 0; i < set.active.length; i++) { uint32 poolId = set.active[i]; if (poolId == 0) break; protocol.updatePoolPriceCache(poolId); } } /** * Internal view methods */ /// @notice returns account market value of active positions /// @param set VTokenPositionSet /// @param protocol platform constants /// @return accountMarketValue - value of all active positions function getAccountMarketValue(VTokenPosition.Set storage set, Protocol.Info storage protocol) internal view returns (int256 accountMarketValue) { for (uint8 i = 0; i < set.active.length; i++) { uint32 poolId = set.active[i]; if (poolId == 0) break; // IVToken vToken = protocol[poolId].vToken; VTokenPosition.Info storage position = set.positions[poolId]; (, uint256 virtualPriceX128) = protocol.getCachedTwapPricesWithDeviationCheck(poolId); uint160 virtualSqrtPriceX96 = virtualPriceX128.toSqrtPriceX96(); //Value of token position for current vToken accountMarketValue += position.marketValue(poolId, virtualPriceX128, protocol); //Value of all active range position for the current vToken accountMarketValue += position.liquidityPositions.marketValue(virtualSqrtPriceX96, poolId, protocol); } // Value of the vQuote token balance accountMarketValue += set.vQuoteBalance; } /// @notice gets information about the token and liquidity positions for all the pools /// @param set VTokenPositionSet /// @return vQuoteBalance vQuote balance for the token position /// @return vTokenPositions array of vToken position function getInfo(VTokenPosition.Set storage set) internal view returns (int256 vQuoteBalance, IClearingHouseStructures.VTokenPositionView[] memory vTokenPositions) { vQuoteBalance = set.vQuoteBalance; uint256 numberOfTokenPositions = set.active.numberOfNonZeroElements(); vTokenPositions = new IClearingHouseStructures.VTokenPositionView[](numberOfTokenPositions); for (uint256 i = 0; i < numberOfTokenPositions; i++) { vTokenPositions[i].poolId = set.active[i]; vTokenPositions[i].balance = set.positions[set.active[i]].balance; vTokenPositions[i].netTraderPosition = set.positions[set.active[i]].netTraderPosition; vTokenPositions[i].sumALastX128 = set.positions[set.active[i]].sumALastX128; vTokenPositions[i].liquidityPositions = set.positions[set.active[i]].liquidityPositions.getInfo(); } } /// @notice returns the long and short side risk for range positions of a particular token /// @param set VTokenPositionSet /// @param isInitialMargin specifies to use initial margin factor (true) or maintainance margin factor (false) /// @param poolId id of the rage trade pool /// @param protocol platform constants /// @return longSideRisk - risk if the token price goes down /// @return shortSideRisk - risk if the token price goes up function getLongShortSideRisk( VTokenPosition.Set storage set, bool isInitialMargin, uint32 poolId, Protocol.Info storage protocol ) internal view returns (int256 longSideRisk, int256 shortSideRisk) { VTokenPosition.Info storage position = set.positions[poolId]; (, uint256 virtualPriceX128) = protocol.getCachedTwapPricesWithDeviationCheck(poolId); uint160 virtualSqrtPriceX96 = virtualPriceX128.toSqrtPriceX96(); uint16 marginRatio = protocol.getMarginRatioBps(poolId, isInitialMargin); int256 tokenPosition = position.balance; int256 longSideRiskRanges = position.liquidityPositions.longSideRisk(virtualSqrtPriceX96).toInt256(); longSideRisk = SignedMath .max(position.netTraderPosition.mulDiv(virtualPriceX128, FixedPoint128.Q128) + longSideRiskRanges, 0) .mulDiv(marginRatio, 1e4); shortSideRisk = SignedMath.max(-tokenPosition, 0).mulDiv(virtualPriceX128, FixedPoint128.Q128).mulDiv( marginRatio, 1e4 ); return (longSideRisk, shortSideRisk); } /// @notice gets the net position for the given poolId /// @param set VTokenPositionSet /// @param poolId id of the rage trade pool /// @param protocol platform constants /// @return netPosition net position of the account for the pool function getNetPosition( VTokenPosition.Set storage set, uint32 poolId, Protocol.Info storage protocol ) internal view returns (int256 netPosition) { if (!set.active.exists(poolId)) return 0; VTokenPosition.Info storage tokenPosition = set.positions[poolId]; return tokenPosition.getNetPosition(poolId, protocol); } /// @notice returns the long and short side risk for range positions of a particular token /// @param set VTokenPositionSet /// @param isInitialMargin specifies to use initial margin factor (true) or maintainance margin factor (false) /// @param protocol platform constants /// @return requiredMargin - required margin value based on the current active positions function getRequiredMargin( VTokenPosition.Set storage set, bool isInitialMargin, Protocol.Info storage protocol ) internal view returns (int256 requiredMargin) { int256 longSideRiskTotal; int256 shortSideRiskTotal; int256 longSideRisk; int256 shortSideRisk; for (uint8 i = 0; i < set.active.length; i++) { if (set.active[i] == 0) break; uint32 poolId = set.active[i]; (longSideRisk, shortSideRisk) = set.getLongShortSideRisk(isInitialMargin, poolId, protocol); if (protocol.isPoolCrossMargined(poolId)) { longSideRiskTotal += longSideRisk; shortSideRiskTotal += shortSideRisk; } else { requiredMargin += SignedMath.max(longSideRisk, shortSideRisk); } } requiredMargin += SignedMath.max(longSideRiskTotal, shortSideRiskTotal); } /// @notice get or create token position /// @dev activates inactive vToken if isCreateNew is true else reverts /// @param set VTokenPositionSet /// @param poolId id of the rage trade pool /// @param createNew if 'vToken' is inactive then activates (true) else reverts with TokenInactive(false) /// @return position - VTokenPosition corresponding to 'vToken' function getTokenPosition( VTokenPosition.Set storage set, uint32 poolId, bool createNew ) internal returns (VTokenPosition.Info storage position) { if (createNew) { set.activate(poolId); } else if (!set.active.exists(poolId)) { revert VPS_TokenInactive(poolId); } position = set.positions[poolId]; } /// @notice returns true if the set does not have any token position active /// @param set VTokenPositionSet /// @return True if there are no active positions function isEmpty(VTokenPosition.Set storage set) internal view returns (bool) { return set.active.isEmpty(); } /// @notice returns true if range position is active for 'vToken' /// @param set VTokenPositionSet /// @param poolId poolId of the vToken /// @return isRangeActive - True if the range position is active function isTokenRangeActive(VTokenPosition.Set storage set, uint32 poolId) internal returns (bool isRangeActive) { VTokenPosition.Info storage vTokenPosition = set.getTokenPosition(poolId, false); isRangeActive = !vTokenPosition.liquidityPositions.isEmpty(); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import { IUniswapV3Pool } from '@uniswap/v3-core-0.8-support/contracts/interfaces/IUniswapV3Pool.sol'; import { IOracle } from '../IOracle.sol'; import { IVToken } from '../IVToken.sol'; import { IVPoolWrapper } from '../IVPoolWrapper.sol'; import { IClearingHouseEnums } from './IClearingHouseEnums.sol'; interface IClearingHouseStructures is IClearingHouseEnums { struct BalanceAdjustments { int256 vQuoteIncrease; // specifies the increase in vQuote balance int256 vTokenIncrease; // specifies the increase in token balance int256 traderPositionIncrease; // specifies the increase in trader position } struct Collateral { IERC20 token; // address of the collateral token CollateralSettings settings; // collateral settings, changable by governance later } struct CollateralSettings { IOracle oracle; // address of oracle which gives price to be used for collateral uint32 twapDuration; // duration of the twap in seconds bool isAllowedForDeposit; // whether the collateral is allowed to be deposited at the moment } struct CollateralDepositView { IERC20 collateral; // address of the collateral token uint256 balance; // balance of the collateral in the account } struct LiquidityChangeParams { int24 tickLower; // tick lower of the range int24 tickUpper; // tick upper of the range int128 liquidityDelta; // positive to add liquidity, negative to remove liquidity uint160 sqrtPriceCurrent; // hint for virtual price, to prevent sandwitch attack uint16 slippageToleranceBps; // slippage tolerance in bps, to prevent sandwitch attack bool closeTokenPosition; // whether to close the token position generated due to the liquidity change LimitOrderType limitOrderType; // limit order type bool settleProfit; // whether to settle profit against USDC margin } struct LiquidityPositionView { int24 tickLower; // tick lower of the range int24 tickUpper; // tick upper of the range uint128 liquidity; // liquidity in the range by the account int256 vTokenAmountIn; // amount of token supplied by the account, to calculate net position int256 sumALastX128; // checkpoint of the term A in funding payment math int256 sumBInsideLastX128; // checkpoint of the term B in funding payment math int256 sumFpInsideLastX128; // checkpoint of the term Fp in funding payment math uint256 sumFeeInsideLastX128; // checkpoint of the trading fees LimitOrderType limitOrderType; // limit order type } struct LiquidationParams { uint16 rangeLiquidationFeeFraction; // fraction of net token position rm from the range to be charged as liquidation fees (in 1e5) uint16 tokenLiquidationFeeFraction; // fraction of traded amount of vquote to be charged as liquidation fees (in 1e5) uint16 closeFactorMMThresholdBps; // fraction the MM threshold for partial liquidation (in 1e4) uint16 partialLiquidationCloseFactorBps; // fraction the % of position to be liquidated if partial liquidation should occur (in 1e4) uint16 insuranceFundFeeShareBps; // fraction of the fee share for insurance fund out of the total liquidation fee (in 1e4) uint16 liquidationSlippageSqrtToleranceBps; // fraction of the max sqrt price slippage threshold (in 1e4) (can be set to - actual price slippage tolerance / 2) uint64 maxRangeLiquidationFees; // maximum range liquidation fees (in settlement token amount decimals) uint64 minNotionalLiquidatable; // minimum notional value of position for it to be eligible for partial liquidation (in settlement token amount decimals) } struct MulticallOperation { MulticallOperationType operationType; // operation type bytes data; // abi encoded data for the operation } struct Pool { IVToken vToken; // address of the vToken, poolId = vToken.truncate() IUniswapV3Pool vPool; // address of the UniswapV3Pool(token0=vToken, token1=vQuote, fee=500) IVPoolWrapper vPoolWrapper; // wrapper address PoolSettings settings; // pool settings, which can be updated by governance later } struct PoolSettings { uint16 initialMarginRatioBps; // margin ratio (1e4) considered for create/update position, removing margin or profit uint16 maintainanceMarginRatioBps; // margin ratio (1e4) considered for liquidations by keeper uint16 maxVirtualPriceDeviationRatioBps; // maximum deviation (1e4) from the current virtual price uint32 twapDuration; // twap duration (seconds) for oracle bool isAllowedForTrade; // whether the pool is allowed to be traded at the moment bool isCrossMargined; // whether cross margined is done for positions of this pool IOracle oracle; // spot price feed twap oracle for this pool } struct SwapParams { int256 amount; // amount of tokens/vQuote to swap uint160 sqrtPriceLimit; // threshold sqrt price which should not be crossed bool isNotional; // whether the amount represents vQuote amount bool isPartialAllowed; // whether to end swap (partial) when sqrtPriceLimit is reached, instead of reverting bool settleProfit; // whether to settle profit against USDC margin } struct TickRange { int24 tickLower; int24 tickUpper; } struct VTokenPositionView { uint32 poolId; // id of the pool of which this token position is for int256 balance; // vTokenLong - vTokenShort int256 netTraderPosition; // net position due to trades and liquidity change carries int256 sumALastX128; // checkoint of the term A in funding payment math LiquidityPositionView[] liquidityPositions; // liquidity positions of the account in the pool } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IClearingHouseEnums { enum LimitOrderType { NONE, LOWER_LIMIT, UPPER_LIMIT } enum MulticallOperationType { UPDATE_MARGIN, UPDATE_PROFIT, SWAP_TOKEN, UPDATE_RANGE_ORDER, REMOVE_LIMIT_ORDER, LIQUIDATE_LIQUIDITY_POSITIONS, LIQUIDATE_TOKEN_POSITION } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface IVQuote is IERC20 { function mint(address account, uint256 amount) external; function burn(uint256 amount) external; function authorize(address vPoolWrapper) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface IVToken is IERC20 { function mint(address account, uint256 amount) external; function burn(uint256 amount) external; function setVPoolWrapper(address) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @title Uint32 length 8 array functions /// @dev Fits in one storage slot library Uint32L8ArrayLib { using Uint32L8ArrayLib for uint32[8]; uint8 constant LENGTH = 8; error U32L8_IllegalElement(uint32 element); error U32L8_NoSpaceLeftToInsert(uint32 element); /// @notice Inserts an element in the array /// @dev Replaces a zero value in the array with element /// @param array Array to modify /// @param element Element to insert function include(uint32[8] storage array, uint32 element) internal { if (element == 0) { revert U32L8_IllegalElement(0); } uint256 emptyIndex = LENGTH; // LENGTH is an invalid index for (uint256 i; i < LENGTH; i++) { if (array[i] == element) { // if element already exists in the array, do nothing return; } // if we found an empty slot, remember it if (array[i] == uint32(0)) { emptyIndex = i; break; } } // if empty index is still LENGTH, there is no space left to insert if (emptyIndex == LENGTH) { revert U32L8_NoSpaceLeftToInsert(element); } array[emptyIndex] = element; } /// @notice Excludes the element from the array /// @dev If element exists, it swaps with last element and makes last element zero /// @param array Array to modify /// @param element Element to remove function exclude(uint32[8] storage array, uint32 element) internal { if (element == 0) { revert U32L8_IllegalElement(0); } uint256 elementIndex = LENGTH; // LENGTH is an invalid index uint256 i; for (; i < LENGTH; i++) { if (array[i] == element) { // element index in the array elementIndex = i; } if (array[i] == 0) { // last non-zero element i = i > 0 ? i - 1 : 0; break; } } // if array is full, i == LENGTH // hence swapping with element at last index i = i == LENGTH ? LENGTH - 1 : i; if (elementIndex != LENGTH) { if (i == elementIndex) { // if element is last element, simply make it zero array[elementIndex] = 0; } else { // move last to element's place and empty lastIndex slot (array[elementIndex], array[i]) = (array[i], 0); } } } /// @notice Returns the index of the element in the array /// @param array Array to perform search on /// @param element Element to search /// @return index if exists or LENGTH otherwise function indexOf(uint32[8] storage array, uint32 element) internal view returns (uint8) { for (uint8 i; i < LENGTH; i++) { if (array[i] == element) { return i; } } return LENGTH; // LENGTH is an invalid index } /// @notice Checks whether the element exists in the array /// @param array Array to perform search on /// @param element Element to search /// @return True if element is found, false otherwise function exists(uint32[8] storage array, uint32 element) internal view returns (bool) { return array.indexOf(element) != LENGTH; // LENGTH is an invalid index } /// @notice Returns length of array (number of non-zero elements) /// @param array Array to perform search on /// @return Length of array function numberOfNonZeroElements(uint32[8] storage array) internal view returns (uint256) { for (uint8 i; i < LENGTH; i++) { if (array[i] == 0) { return i; } } return LENGTH; } /// @notice Checks whether the array is empty or not /// @param array Array to perform search on /// @return True if the set does not have any token position active function isEmpty(uint32[8] storage array) internal view returns (bool) { return array[0] == 0; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import {IUniswapV3PoolImmutables} from './pool/IUniswapV3PoolImmutables.sol'; import {IUniswapV3PoolState} from './pool/IUniswapV3PoolState.sol'; import {IUniswapV3PoolDerivedState} from './pool/IUniswapV3PoolDerivedState.sol'; import {IUniswapV3PoolActions} from './pool/IUniswapV3PoolActions.sol'; import {IUniswapV3PoolOwnerActions} from './pool/IUniswapV3PoolOwnerActions.sol'; import {IUniswapV3PoolErrors} from './pool/IUniswapV3PoolErrors.sol'; import {IUniswapV3PoolEvents} from './pool/IUniswapV3PoolEvents.sol'; /// @title The interface for a Uniswap V3 Pool /// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform /// to the ERC20 specification /// @dev The pool interface is broken up into many smaller pieces interface IUniswapV3Pool is IUniswapV3PoolImmutables, IUniswapV3PoolState, IUniswapV3PoolDerivedState, IUniswapV3PoolActions, IUniswapV3PoolOwnerActions, IUniswapV3PoolErrors, IUniswapV3PoolEvents { }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.4; import { IUniswapV3Pool } from '@uniswap/v3-core-0.8-support/contracts/interfaces/IUniswapV3Pool.sol'; import { IVQuote } from './IVQuote.sol'; import { IVToken } from './IVToken.sol'; interface IVPoolWrapper { struct InitializeVPoolWrapperParams { address clearingHouse; // address of clearing house contract (proxy) IVToken vToken; // address of vToken contract IVQuote vQuote; // address of vQuote contract IUniswapV3Pool vPool; // address of Uniswap V3 Pool contract, created using vToken and vQuote uint24 liquidityFeePips; // liquidity fee fraction (in 1e6) uint24 protocolFeePips; // protocol fee fraction (in 1e6) } struct SwapResult { int256 amountSpecified; // amount of tokens/vQuote which were specified in the swap request int256 vTokenIn; // actual amount of vTokens paid by account to the Pool int256 vQuoteIn; // actual amount of vQuotes paid by account to the Pool uint256 liquidityFees; // actual amount of fees paid by account to the Pool uint256 protocolFees; // actual amount of fees paid by account to the Protocol uint160 sqrtPriceX96Start; // sqrt price at the beginning of the swap uint160 sqrtPriceX96End; // sqrt price at the end of the swap } struct WrapperValuesInside { int256 sumAX128; // sum of all the A terms in the pool int256 sumBInsideX128; // sum of all the B terms in side the tick range in the pool int256 sumFpInsideX128; // sum of all the Fp terms in side the tick range in the pool uint256 sumFeeInsideX128; // sum of all the fee terms in side the tick range in the pool } /// @notice Emitted whenever a swap takes place /// @param swapResult the swap result values event Swap(SwapResult swapResult); /// @notice Emitted whenever liquidity is added /// @param tickLower the lower tick of the range /// @param tickUpper the upper tick of the range /// @param liquidity the amount of liquidity that was added /// @param vTokenPrincipal the amount of vToken that was sent to UniswapV3Pool /// @param vQuotePrincipal the mount of vQuote charged was sent to UniswapV3Pool event Mint(int24 tickLower, int24 tickUpper, uint128 liquidity, uint256 vTokenPrincipal, uint256 vQuotePrincipal); /// @notice Emitted whenever liquidity is removed /// @param tickLower the lower tick of the range /// @param tickUpper the upper tick of the range /// @param liquidity the amount of liquidity that was removed /// @param vTokenPrincipal the amount of vToken that was received from UniswapV3Pool /// @param vQuotePrincipal the mount of vQuote charged was received from UniswapV3Pool event Burn(int24 tickLower, int24 tickUpper, uint128 liquidity, uint256 vTokenPrincipal, uint256 vQuotePrincipal); /// @notice Emitted whenever clearing house enquired about the accrued protocol fees /// @param amount the amount of accrued protocol fees event AccruedProtocolFeeCollected(uint256 amount); /// @notice Emitted when governance updates the liquidity fees /// @param liquidityFeePips the new liquidity fee ratio event LiquidityFeeUpdated(uint24 liquidityFeePips); /// @notice Emitted when governance updates the protocol fees /// @param protocolFeePips the new protocol fee ratio event ProtocolFeeUpdated(uint24 protocolFeePips); /// @notice Emitted when funding rate override is updated /// @param fundingRateOverrideX128 the new funding rate override value event FundingRateOverrideUpdated(int256 fundingRateOverrideX128); function initialize(InitializeVPoolWrapperParams memory params) external; function vPool() external view returns (IUniswapV3Pool); function getValuesInside(int24 tickLower, int24 tickUpper) external view returns (WrapperValuesInside memory wrapperValuesInside); function getExtrapolatedValuesInside(int24 tickLower, int24 tickUpper) external view returns (WrapperValuesInside memory wrapperValuesInside); function swap( bool swapVTokenForVQuote, // zeroForOne int256 amountSpecified, uint160 sqrtPriceLimitX96 ) external returns (SwapResult memory swapResult); function mint( int24 tickLower, int24 tickUpper, uint128 liquidity ) external returns ( uint256 vTokenPrincipal, uint256 vQuotePrincipal, WrapperValuesInside memory wrapperValuesInside ); function burn( int24 tickLower, int24 tickUpper, uint128 liquidity ) external returns ( uint256 vTokenPrincipal, uint256 vQuotePrincipal, WrapperValuesInside memory wrapperValuesInside ); function getSumAX128() external view returns (int256); function getExtrapolatedSumAX128() external view returns (int256); function liquidityFeePips() external view returns (uint24); function protocolFeePips() external view returns (uint24); /// @notice Used by clearing house to update funding rate when clearing house is paused or unpaused. /// @param useZeroFundingRate: used to discount funding payment during the duration ch was paused. function updateGlobalFundingState(bool useZeroFundingRate) external; /// @notice Used by clearing house to know how much protocol fee was collected. /// @return accruedProtocolFeeLast amount of protocol fees accrued since last collection. /// @dev Does not do any token transfer, just reduces the state in wrapper by accruedProtocolFeeLast. /// Clearing house already has the amount of settlement tokens to send to treasury. function collectAccruedProtocolFee() external returns (uint256 accruedProtocolFeeLast); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.4; import { FullMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/FullMath.sol'; import { FixedPoint96 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint96.sol'; import { TickMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/TickMath.sol'; import { Bisection } from './Bisection.sol'; /// @title Price math functions library PriceMath { using FullMath for uint256; error IllegalSqrtPrice(uint160 sqrtPriceX96); /// @notice Computes the square of a sqrtPriceX96 value /// @param sqrtPriceX96: the square root of the input price in Q96 format /// @return priceX128 : input price in Q128 format function toPriceX128(uint160 sqrtPriceX96) internal pure returns (uint256 priceX128) { if (sqrtPriceX96 < TickMath.MIN_SQRT_RATIO || sqrtPriceX96 >= TickMath.MAX_SQRT_RATIO) { revert IllegalSqrtPrice(sqrtPriceX96); } priceX128 = _toPriceX128(sqrtPriceX96); } /// @notice computes the square of a sqrtPriceX96 value /// @param sqrtPriceX96: input price in Q128 format function _toPriceX128(uint160 sqrtPriceX96) private pure returns (uint256 priceX128) { priceX128 = uint256(sqrtPriceX96).mulDiv(sqrtPriceX96, 1 << 64); } /// @notice computes the square root of a priceX128 value /// @param priceX128: input price in Q128 format /// @return sqrtPriceX96 : the square root of the input price in Q96 format function toSqrtPriceX96(uint256 priceX128) internal pure returns (uint160 sqrtPriceX96) { // Uses bisection method to find solution to the equation toPriceX128(x) = priceX128 sqrtPriceX96 = Bisection.findSolution( _toPriceX128, priceX128, /// @dev sqrtPriceX96 is always bounded by MIN_SQRT_RATIO and MAX_SQRT_RATIO. /// If solution falls outside of these bounds, findSolution method reverts TickMath.MIN_SQRT_RATIO, TickMath.MAX_SQRT_RATIO - 1 ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @title Safe cast functions library SafeCast { error SafeCast_Int128Overflow(uint128 value); function toInt128(uint128 y) internal pure returns (int128 z) { unchecked { if (y >= 2**127) revert SafeCast_Int128Overflow(y); z = int128(y); } } error SafeCast_Int256Overflow(uint256 value); function toInt256(uint256 y) internal pure returns (int256 z) { unchecked { if (y >= 2**255) revert SafeCast_Int256Overflow(y); z = int256(y); } } error SafeCast_UInt224Overflow(uint256 value); function toUint224(uint256 y) internal pure returns (uint224 z) { if (y > 2**224) revert SafeCast_UInt224Overflow(y); z = uint224(y); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.4; import { TickMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/TickMath.sol'; import { IUniswapV3Pool } from '@uniswap/v3-core-0.8-support/contracts/interfaces/IUniswapV3Pool.sol'; /// @title UniswapV3Pool helper functions library UniswapV3PoolHelper { using UniswapV3PoolHelper for IUniswapV3Pool; error UV3PH_OracleConsultFailed(); /// @notice Get the pool's current tick /// @param v3Pool The uniswap v3 pool contract /// @return tick the current tick function tickCurrent(IUniswapV3Pool v3Pool) internal view returns (int24 tick) { (, tick, , , , , ) = v3Pool.slot0(); } /// @notice Get the pool's current sqrt price /// @param v3Pool The uniswap v3 pool contract /// @return sqrtPriceX96 the current sqrt price function sqrtPriceCurrent(IUniswapV3Pool v3Pool) internal view returns (uint160 sqrtPriceX96) { (sqrtPriceX96, , , , , , ) = v3Pool.slot0(); } /// @notice Get twap price for uniswap v3 pool /// @param v3Pool The uniswap v3 pool contract /// @param twapDuration The twap period /// @return sqrtPriceX96 the twap price function twapSqrtPrice(IUniswapV3Pool v3Pool, uint32 twapDuration) internal view returns (uint160 sqrtPriceX96) { int24 _twapTick = v3Pool.twapTick(twapDuration); sqrtPriceX96 = TickMath.getSqrtRatioAtTick(_twapTick); } /// @notice Get twap tick for uniswap v3 pool /// @param v3Pool The uniswap v3 pool contract /// @param twapDuration The twap period /// @return _twapTick the twap tick function twapTick(IUniswapV3Pool v3Pool, uint32 twapDuration) internal view returns (int24 _twapTick) { if (twapDuration == 0) { return v3Pool.tickCurrent(); } uint32[] memory secondAgos = new uint32[](2); secondAgos[0] = twapDuration; secondAgos[1] = 0; // this call will fail if period is bigger than MaxObservationPeriod try v3Pool.observe(secondAgos) returns (int56[] memory tickCumulatives, uint160[] memory) { int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; int24 timeWeightedAverageTick = int24(tickCumulativesDelta / int56(uint56(twapDuration))); // Always round to negative infinity if (tickCumulativesDelta < 0 && (tickCumulativesDelta % int56(uint56(twapDuration)) != 0)) { timeWeightedAverageTick--; } return timeWeightedAverageTick; } catch { // if for some reason v3Pool.observe fails, fallback to the current tick (, _twapTick, , , , , ) = v3Pool.slot0(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ArbSys { /** * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) * @return block number as int */ function arbBlockNumber() external view returns (uint256); } /// @title Library for getting block number for the current chain library Block { /// @notice Get block number /// @return block number as uint32 function number() internal view returns (uint32) { uint256 chainId = block.chainid; if (chainId == 42161 || chainId == 421611 || chainId == 421612) { return uint32(ArbSys(address(100)).arbBlockNumber()); } else { return uint32(block.number); } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that never changes /// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values interface IUniswapV3PoolImmutables { /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface /// @return The contract address function factory() external view returns (address); /// @notice The first of the two tokens of the pool, sorted by address /// @return The token contract address function token0() external view returns (address); /// @notice The second of the two tokens of the pool, sorted by address /// @return The token contract address function token1() external view returns (address); /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 /// @return The fee function fee() external view returns (uint24); /// @notice The pool tick spacing /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... /// This value is an int24 to avoid casting even though it is always positive. /// @return The tick spacing function tickSpacing() external view returns (int24); /// @notice The maximum amount of position liquidity that can use any tick in the range /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool /// @return The max amount of liquidity per tick function maxLiquidityPerTick() external view returns (uint128); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that can change /// @notice These methods compose the pool's state, and can change with any frequency including multiple times /// per transaction interface IUniswapV3PoolState { /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas /// when accessed externally. /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value /// @return tick The current tick of the pool, i.e. according to the last tick transition that was run. /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick /// boundary. /// @return observationIndex The index of the last oracle observation that was written, /// @return observationCardinality The current maximum number of observations stored in the pool, /// @return observationCardinalityNext The next maximum number of observations, to be updated when the observation. /// @return feeProtocol The protocol fee for both tokens of the pool. /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. /// unlocked Whether the pool is currently locked to reentrancy function slot0() external view returns ( uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked ); /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool /// @dev This value can overflow the uint256 function feeGrowthGlobal0X128() external view returns (uint256); /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool /// @dev This value can overflow the uint256 function feeGrowthGlobal1X128() external view returns (uint256); /// @notice The amounts of token0 and token1 that are owed to the protocol /// @dev Protocol fees will never exceed uint128 max in either token function protocolFees() external view returns (uint128 token0, uint128 token1); /// @notice The currently in range liquidity available to the pool /// @dev This value has no relationship to the total liquidity across all ticks /// @return The liquidity at the current price of the pool function liquidity() external view returns (uint128); /// @notice Look up information about a specific tick in the pool /// @param tick The tick to look up /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or /// tick upper /// @return liquidityNet how much liquidity changes when the pool price crosses the tick, /// @return feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, /// @return feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, /// @return tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick /// @return secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, /// @return secondsOutside the seconds spent on the other side of the tick from the current tick, /// @return initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. /// In addition, these values are only relative and must be used only in comparison to previous snapshots for /// a specific position. function ticks(int24 tick) external view returns ( uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128, int56 tickCumulativeOutside, uint160 secondsPerLiquidityOutsideX128, uint32 secondsOutside, bool initialized ); /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information function tickBitmap(int16 wordPosition) external view returns (uint256); /// @notice Returns the information about a position by the position's key /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper /// @return liquidity The amount of liquidity in the position, /// @return feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, /// @return feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, /// @return tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, /// @return tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke function positions(bytes32 key) external view returns ( uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, uint128 tokensOwed0, uint128 tokensOwed1 ); /// @notice Returns data about a specific observation index /// @param index The element of the observations array to fetch /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time /// ago, rather than at a specific index in the array. /// @return blockTimestamp The timestamp of the observation, /// @return tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, /// @return secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, /// @return initialized whether the observation has been initialized and the values are safe to use function observations(uint256 index) external view returns ( uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, bool initialized ); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that is not stored /// @notice Contains view functions to provide information about the pool that is computed rather than stored on the /// blockchain. The functions here may have variable gas costs. interface IUniswapV3PoolDerivedState { /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, /// you must call it with secondsAgos = [3600, 0]. /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block /// timestamp function observe(uint32[] calldata secondsAgos) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first /// snapshot is taken and the second snapshot is taken. /// @param tickLower The lower tick of the range /// @param tickUpper The upper tick of the range /// @return tickCumulativeInside The snapshot of the tick accumulator for the range /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range /// @return secondsInside The snapshot of seconds per liquidity for the range function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) external view returns ( int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside ); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Permissionless pool actions /// @notice Contains pool methods that can be called by anyone interface IUniswapV3PoolActions { /// @notice Sets the initial price for the pool /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 function initialize(uint160 sqrtPriceX96) external; /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends /// on tickLower, tickUpper, the amount of liquidity, and the current price. /// @param recipient The address for which the liquidity will be created /// @param tickLower The lower tick of the position in which to add liquidity /// @param tickUpper The upper tick of the position in which to add liquidity /// @param amount The amount of liquidity to mint /// @param data Any data that should be passed through to the callback /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback function mint( address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data ) external returns (uint256 amount0, uint256 amount1); /// @notice Collects tokens owed to a position /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. /// @param recipient The address which should receive the fees collected /// @param tickLower The lower tick of the position for which to collect fees /// @param tickUpper The upper tick of the position for which to collect fees /// @param amount0Requested How much token0 should be withdrawn from the fees owed /// @param amount1Requested How much token1 should be withdrawn from the fees owed /// @return amount0 The amount of fees collected in token0 /// @return amount1 The amount of fees collected in token1 function collect( address recipient, int24 tickLower, int24 tickUpper, uint128 amount0Requested, uint128 amount1Requested ) external returns (uint128 amount0, uint128 amount1); /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 /// @dev Fees must be collected separately via a call to #collect /// @param tickLower The lower tick of the position for which to burn liquidity /// @param tickUpper The upper tick of the position for which to burn liquidity /// @param amount How much liquidity to burn /// @return amount0 The amount of token0 sent to the recipient /// @return amount1 The amount of token1 sent to the recipient function burn( int24 tickLower, int24 tickUpper, uint128 amount ) external returns (uint256 amount0, uint256 amount1); /// @notice Swap token0 for token1, or token1 for token0 /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback /// @param recipient The address to receive the output of the swap /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this /// value after the swap. If one for zero, the price cannot be greater than this value after the swap /// @param data Any data to be passed through to the callback /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive function swap( address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes calldata data ) external returns (int256 amount0, int256 amount1); /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling /// with 0 amount{0,1} and sending the donation amount(s) from the callback /// @param recipient The address which will receive the token0 and token1 amounts /// @param amount0 The amount of token0 to send /// @param amount1 The amount of token1 to send /// @param data Any data to be passed through to the callback function flash( address recipient, uint256 amount0, uint256 amount1, bytes calldata data ) external; /// @notice Increase the maximum number of price and liquidity observations that this pool will store /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to /// the input observationCardinalityNext. /// @param observationCardinalityNext The desired minimum number of observations for the pool to store function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Permissioned pool actions /// @notice Contains pool methods that may only be called by the factory owner interface IUniswapV3PoolOwnerActions { /// @notice Set the denominator of the protocol's % share of the fees /// @param feeProtocol0 new protocol fee for token0 of the pool /// @param feeProtocol1 new protocol fee for token1 of the pool function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; /// @notice Collect the protocol fee accrued to the pool /// @param recipient The address to which collected protocol fees should be sent /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 /// @return amount0 The protocol fee collected in token0 /// @return amount1 The protocol fee collected in token1 function collectProtocol( address recipient, uint128 amount0Requested, uint128 amount1Requested ) external returns (uint128 amount0, uint128 amount1); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Errors emitted by a pool /// @notice Contains all events emitted by the pool interface IUniswapV3PoolErrors { error LOK(); error TLU(); error TLM(); error TUM(); error AI(); error M0(); error M1(); error AS(); error IIA(); error L(); error F0(); error F1(); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Events emitted by a pool /// @notice Contains all events emitted by the pool interface IUniswapV3PoolEvents { /// @notice Emitted exactly once by a pool when #initialize is first called on the pool /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool event Initialize(uint160 sqrtPriceX96, int24 tick); /// @notice Emitted when liquidity is minted for a given position /// @param sender The address that minted the liquidity /// @param owner The owner of the position and recipient of any minted liquidity /// @param tickLower The lower tick of the position /// @param tickUpper The upper tick of the position /// @param amount The amount of liquidity minted to the position range /// @param amount0 How much token0 was required for the minted liquidity /// @param amount1 How much token1 was required for the minted liquidity event Mint( address sender, address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1 ); /// @notice Emitted when fees are collected by the owner of a position /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees /// @param owner The owner of the position for which fees are collected /// @param tickLower The lower tick of the position /// @param tickUpper The upper tick of the position /// @param amount0 The amount of token0 fees collected /// @param amount1 The amount of token1 fees collected event Collect( address indexed owner, address recipient, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount0, uint128 amount1 ); /// @notice Emitted when a position's liquidity is removed /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect /// @param owner The owner of the position for which liquidity is removed /// @param tickLower The lower tick of the position /// @param tickUpper The upper tick of the position /// @param amount The amount of liquidity to remove /// @param amount0 The amount of token0 withdrawn /// @param amount1 The amount of token1 withdrawn event Burn( address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1 ); /// @notice Emitted by the pool for any swaps between token0 and token1 /// @param sender The address that initiated the swap call, and that received the callback /// @param recipient The address that received the output of the swap /// @param amount0 The delta of the token0 balance of the pool /// @param amount1 The delta of the token1 balance of the pool /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 /// @param liquidity The liquidity of the pool after the swap /// @param tick The log base 1.0001 of price of the pool after the swap event Swap( address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick ); /// @notice Emitted by the pool for any flashes of token0/token1 /// @param sender The address that initiated the swap call, and that received the callback /// @param recipient The address that received the tokens from flash /// @param amount0 The amount of token0 that was flashed /// @param amount1 The amount of token1 that was flashed /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee event Flash( address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1 ); /// @notice Emitted by the pool for increases to the number of observations that can be stored /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index /// just before a mint/swap/burn. /// @param observationCardinalityNextOld The previous value of the next observation cardinality /// @param observationCardinalityNextNew The updated value of the next observation cardinality event IncreaseObservationCardinalityNext( uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew ); /// @notice Emitted when the protocol fee is changed by the pool /// @param feeProtocol0Old The previous value of the token0 protocol fee /// @param feeProtocol1Old The previous value of the token1 protocol fee /// @param feeProtocol0New The updated value of the token0 protocol fee /// @param feeProtocol1New The updated value of the token1 protocol fee event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New); /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner /// @param sender The address that collects the protocol fees /// @param recipient The address that receives the collected protocol fees /// @param amount0 The amount of token0 protocol fees that is withdrawn /// @param amount0 The amount of token1 protocol fees that is withdrawn event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1); }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.0; interface IOracle { function getTwapPriceX128(uint32 twapDuration) external view returns (uint256 priceX128); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.4.0; /// @title FixedPoint96 /// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) /// @dev Used in SqrtPriceMath.sol library FixedPoint96 { uint8 internal constant RESOLUTION = 96; uint256 internal constant Q96 = 0x1000000000000000000000000; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @title Math library for computing sqrt prices from ticks and vice versa /// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports /// prices between 2**-128 and 2**128 library TickMath { error T(); error R(); /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 int24 internal constant MIN_TICK = -887272; /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 int24 internal constant MAX_TICK = -MIN_TICK; /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) uint160 internal constant MIN_SQRT_RATIO = 4295128739; /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; /// @notice Calculates sqrt(1.0001^tick) * 2^96 /// @dev Throws if |tick| > max tick /// @param tick The input tick for the above formula /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) /// at the given tick function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { unchecked { uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); if (absTick > uint256(int256(MAX_TICK))) revert T(); uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; if (tick > 0) ratio = type(uint256).max / ratio; // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. // we then downcast because we know the result always fits within 160 bits due to our tick input constraint // we round up in the division so getTickAtSqrtRatio of the output price is always consistent sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); } } /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may /// ever return. /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { unchecked { // second inequality must be < because the price can never reach the price at the max tick if (!(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO)) revert R(); uint256 ratio = uint256(sqrtPriceX96) << 32; uint256 r = ratio; uint256 msb = 0; assembly { let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(5, gt(r, 0xFFFFFFFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(4, gt(r, 0xFFFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(3, gt(r, 0xFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(2, gt(r, 0xF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(1, gt(r, 0x3)) msb := or(msb, f) r := shr(f, r) } assembly { let f := gt(r, 0x1) msb := or(msb, f) } if (msb >= 128) r = ratio >> (msb - 127); else r = ratio << (127 - msb); int256 log_2 = (int256(msb) - 128) << 64; assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(63, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(62, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(61, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(60, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(59, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(58, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(57, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(56, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(55, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(54, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(53, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(52, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(51, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(50, f)) } int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @title Bisection Method /// @notice https://en.wikipedia.org/wiki/Bisection_method library Bisection { error SolutionOutOfBounds(uint256 y_target, uint160 x_lower, uint160 x_upper); /// @notice Finds the solution to the equation f(x) = y_target using the bisection method /// @param f: strictly increasing function f: uint160 -> uint256 /// @param y_target: the target value of f(x) /// @param x_lower: the lower bound for x /// @param x_upper: the upper bound for x /// @return x_target: the rounded down solution to the equation f(x) = y_target function findSolution( function(uint160) pure returns (uint256) f, uint256 y_target, uint160 x_lower, uint160 x_upper ) internal pure returns (uint160) { // compute y at the bounds uint256 y_lower = f(x_lower); uint256 y_upper = f(x_upper); // if y is out of the bounds then revert if (y_target < y_lower || y_target > y_upper) revert SolutionOutOfBounds(y_target, x_lower, x_upper); // bisect repeatedly until the solution is within an error of 1 unit uint256 y_mid; uint160 x_mid; while (x_upper - x_lower > 1) { x_mid = x_lower + (x_upper - x_lower) / 2; y_mid = f(x_mid); if (y_mid > y_target) { x_upper = x_mid; y_upper = y_mid; } else { x_lower = x_mid; y_lower = y_mid; } } // at this point, x_upper - x_lower is either 0 or 1 // if it is 1 then check if x_upper is the solution, else return x_lower as the rounded down solution return x_lower != x_upper && f(x_upper) == y_target ? x_upper : x_lower; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.0; /// @title Uint48 concating functions library Uint48Lib { /// @notice Packs two int24 values into uint48 /// @dev Used for concating two ticks into 48 bits value /// @param val1 First 24 bits value /// @param val2 Second 24 bits value /// @return concatenated value function concat(int24 val1, int24 val2) internal pure returns (uint48 concatenated) { assembly { concatenated := add(shl(24, val1), and(val2, 0x000000ffffff)) } } /// @notice Unpacks uint48 into two int24 values /// @dev Used for unpacking 48 bits value into two 24 bits values /// @param concatenated 48 bits value /// @return val1 First 24 bits value /// @return val2 Second 24 bits value function unconcat(uint48 concatenated) internal pure returns (int24 val1, int24 val2) { assembly { val2 := concatenated val1 := shr(24, concatenated) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @title Uint48 length 5 array functions /// @dev Fits in one storage slot library Uint48L5ArrayLib { using Uint48L5ArrayLib for uint48[5]; uint8 constant LENGTH = 5; error U48L5_IllegalElement(uint48 element); error U48L5_NoSpaceLeftToInsert(uint48 element); /// @notice Inserts an element in the array /// @dev Replaces a zero value in the array with element /// @param array Array to modify /// @param element Element to insert function include(uint48[5] storage array, uint48 element) internal { if (element == 0) { revert U48L5_IllegalElement(0); } uint256 emptyIndex = LENGTH; // LENGTH is an invalid index for (uint256 i; i < LENGTH; i++) { if (array[i] == element) { // if element already exists in the array, do nothing return; } // if we found an empty slot, remember it if (array[i] == uint48(0)) { emptyIndex = i; break; } } // if empty index is still LENGTH, there is no space left to insert if (emptyIndex == LENGTH) { revert U48L5_NoSpaceLeftToInsert(element); } array[emptyIndex] = element; } /// @notice Excludes the element from the array /// @dev If element exists, it swaps with last element and makes last element zero /// @param array Array to modify /// @param element Element to remove function exclude(uint48[5] storage array, uint48 element) internal { if (element == 0) { revert U48L5_IllegalElement(0); } uint256 elementIndex = LENGTH; // LENGTH is an invalid index uint256 i; for (; i < LENGTH; i++) { if (array[i] == element) { // element index in the array elementIndex = i; } if (array[i] == 0) { // last non-zero element i = i > 0 ? i - 1 : 0; break; } } // if array is full, i == LENGTH // hence swapping with element at last index i = i == LENGTH ? LENGTH - 1 : i; if (elementIndex != LENGTH) { if (i == elementIndex) { // if element is last element, simply make it zero array[elementIndex] = 0; } else { // move last to element's place and empty lastIndex slot (array[elementIndex], array[i]) = (array[i], 0); } } } /// @notice Returns the index of the element in the array /// @param array Array to perform search on /// @param element Element to search /// @return index if exists or LENGTH otherwise function indexOf(uint48[5] storage array, uint48 element) internal view returns (uint8) { for (uint8 i; i < LENGTH; i++) { if (array[i] == element) { return i; } } return LENGTH; // LENGTH is an invalid index } /// @notice Checks whether the element exists in the array /// @param array Array to perform search on /// @param element Element to search /// @return True if element is found, false otherwise function exists(uint48[5] storage array, uint48 element) internal view returns (bool) { return array.indexOf(element) != LENGTH; // LENGTH is an invalid index } /// @notice Returns length of array (number of non-zero elements) /// @param array Array to perform search on /// @return Length of array function numberOfNonZeroElements(uint48[5] storage array) internal view returns (uint256) { for (uint8 i; i < LENGTH; i++) { if (array[i] == 0) { return i; } } return LENGTH; } /// @notice Checks whether the array is empty or not /// @param array Array to perform search on /// @return True if the set does not have any token position active function isEmpty(uint48[5] storage array) internal view returns (bool) { return array[0] == 0; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import {SafeCast} from './SafeCast.sol'; import {FullMath} from './FullMath.sol'; import {UnsafeMath} from './UnsafeMath.sol'; import {FixedPoint96} from './FixedPoint96.sol'; /// @title Functions based on Q64.96 sqrt price and liquidity /// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas library SqrtPriceMath { using SafeCast for uint256; /// @notice Gets the next sqrt price given a delta of token0 /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the /// price less in order to not send too much output. /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount). /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta /// @param liquidity The amount of usable liquidity /// @param amount How much of token0 to add or remove from virtual reserves /// @param add Whether to add or remove the amount of token0 /// @return The price after adding or removing amount, depending on add function getNextSqrtPriceFromAmount0RoundingUp( uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add ) internal pure returns (uint160) { // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price if (amount == 0) return sqrtPX96; uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; if (add) { unchecked { uint256 product; if ((product = amount * sqrtPX96) / amount == sqrtPX96) { uint256 denominator = numerator1 + product; if (denominator >= numerator1) // always fits in 160 bits return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator)); } } // denominator is checked for overflow return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96) + amount)); } else { unchecked { uint256 product; // if the product overflows, we know the denominator underflows // in addition, we must check that the denominator does not underflow require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); uint256 denominator = numerator1 - product; return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); } } } /// @notice Gets the next sqrt price given a delta of token1 /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the /// price less in order to not send too much output. /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta /// @param liquidity The amount of usable liquidity /// @param amount How much of token1 to add, or remove, from virtual reserves /// @param add Whether to add, or remove, the amount of token1 /// @return The price after adding or removing `amount` function getNextSqrtPriceFromAmount1RoundingDown( uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add ) internal pure returns (uint160) { // if we're adding (subtracting), rounding down requires rounding the quotient down (up) // in both cases, avoid a mulDiv for most inputs if (add) { uint256 quotient = ( amount <= type(uint160).max ? (amount << FixedPoint96.RESOLUTION) / liquidity : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity) ); return (uint256(sqrtPX96) + quotient).toUint160(); } else { uint256 quotient = ( amount <= type(uint160).max ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity) : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) ); require(sqrtPX96 > quotient); // always fits 160 bits unchecked { return uint160(sqrtPX96 - quotient); } } } /// @notice Gets the next sqrt price given an input amount of token0 or token1 /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount /// @param liquidity The amount of usable liquidity /// @param amountIn How much of token0, or token1, is being swapped in /// @param zeroForOne Whether the amount in is token0 or token1 /// @return sqrtQX96 The price after adding the input amount to token0 or token1 function getNextSqrtPriceFromInput( uint160 sqrtPX96, uint128 liquidity, uint256 amountIn, bool zeroForOne ) internal pure returns (uint160 sqrtQX96) { require(sqrtPX96 > 0); require(liquidity > 0); // round to make sure that we don't pass the target price return zeroForOne ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true); } /// @notice Gets the next sqrt price given an output amount of token0 or token1 /// @dev Throws if price or liquidity are 0 or the next price is out of bounds /// @param sqrtPX96 The starting price before accounting for the output amount /// @param liquidity The amount of usable liquidity /// @param amountOut How much of token0, or token1, is being swapped out /// @param zeroForOne Whether the amount out is token0 or token1 /// @return sqrtQX96 The price after removing the output amount of token0 or token1 function getNextSqrtPriceFromOutput( uint160 sqrtPX96, uint128 liquidity, uint256 amountOut, bool zeroForOne ) internal pure returns (uint160 sqrtQX96) { require(sqrtPX96 > 0); require(liquidity > 0); // round to make sure that we pass the target price return zeroForOne ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false); } /// @notice Gets the amount0 delta between two prices /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) /// @param sqrtRatioAX96 A sqrt price /// @param sqrtRatioBX96 Another sqrt price /// @param liquidity The amount of usable liquidity /// @param roundUp Whether to round the amount up or down /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices function getAmount0Delta( uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity, bool roundUp ) internal pure returns (uint256 amount0) { unchecked { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; require(sqrtRatioAX96 > 0); return roundUp ? UnsafeMath.divRoundingUp( FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), sqrtRatioAX96 ) : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; } } /// @notice Gets the amount1 delta between two prices /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) /// @param sqrtRatioAX96 A sqrt price /// @param sqrtRatioBX96 Another sqrt price /// @param liquidity The amount of usable liquidity /// @param roundUp Whether to round the amount up, or down /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices function getAmount1Delta( uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity, bool roundUp ) internal pure returns (uint256 amount1) { unchecked { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); return roundUp ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); } } /// @notice Helper that gets signed token0 delta /// @param sqrtRatioAX96 A sqrt price /// @param sqrtRatioBX96 Another sqrt price /// @param liquidity The change in liquidity for which to compute the amount0 delta /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices function getAmount0Delta( uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, int128 liquidity ) internal pure returns (int256 amount0) { unchecked { return liquidity < 0 ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); } } /// @notice Helper that gets signed token1 delta /// @param sqrtRatioAX96 A sqrt price /// @param sqrtRatioBX96 Another sqrt price /// @param liquidity The change in liquidity for which to compute the amount1 delta /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices function getAmount1Delta( uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, int128 liquidity ) internal pure returns (int256 amount1) { unchecked { return liquidity < 0 ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.4; import { FixedPoint128 } from '@uniswap/v3-core-0.8-support/contracts/libraries/FixedPoint128.sol'; import { FullMath } from '@uniswap/v3-core-0.8-support/contracts/libraries/FullMath.sol'; import { SafeCast } from './SafeCast.sol'; import { SignedFullMath } from './SignedFullMath.sol'; /// @title Funding payment functions /// @notice Funding Payment Logic used to distribute the FP bill paid by traders among the LPs in the liquidity range library FundingPayment { using FullMath for uint256; using SafeCast for uint256; using SignedFullMath for int256; struct Info { // FR * P * dt int256 sumAX128; // trade token amount / liquidity int256 sumBX128; // sum(a * sumB) int256 sumFpX128; // time when state was last updated uint48 timestampLast; } event FundingPaymentStateUpdated( FundingPayment.Info fundingPayment, int256 fundingRateX128, uint256 virtualPriceX128 ); /// @notice Calculates the funding rate based on prices /// @param realPriceX128 spot price of token /// @param virtualPriceX128 futures price of token function getFundingRate(uint256 realPriceX128, uint256 virtualPriceX128) internal pure returns (int256 fundingRateX128) { int256 priceDeltaX128 = virtualPriceX128.toInt256() - realPriceX128.toInt256(); return priceDeltaX128.mulDiv(FixedPoint128.Q128, realPriceX128) / 1 days; } /// @notice Used to update the state of the funding payment whenever a trade takes place /// @param info pointer to the funding payment state /// @param vTokenAmount trade token amount /// @param liquidity active liquidity in the range during the trade (step) /// @param blockTimestamp timestamp of current block /// @param fundingRateX128 the constant funding rate to apply for the duration between timestampLast and blockTimestamp /// @param virtualPriceX128 futures price of token function update( FundingPayment.Info storage info, int256 vTokenAmount, uint256 liquidity, uint48 blockTimestamp, int256 fundingRateX128, uint256 virtualPriceX128 ) internal { int256 a = nextAX128(info.timestampLast, blockTimestamp, fundingRateX128, virtualPriceX128); info.sumFpX128 += a.mulDivRoundingDown(info.sumBX128, int256(FixedPoint128.Q128)); info.sumAX128 += a; info.sumBX128 += vTokenAmount.mulDiv(int256(FixedPoint128.Q128), int256(liquidity)); info.timestampLast = blockTimestamp; emit FundingPaymentStateUpdated(info, fundingRateX128, virtualPriceX128); } /// @notice Used to get the rate of funding payment for the duration between last trade and this trade /// @dev Positive A value means at this duration, longs pay shorts. Negative means shorts pay longs. /// @param timestampLast start timestamp of duration /// @param blockTimestamp end timestamp of duration /// @param virtualPriceX128 futures price of token /// @param fundingRateX128 the constant funding rate to apply for the duration between timestampLast and blockTimestamp /// @return aX128 value called "a" (see funding payment math documentation) function nextAX128( uint48 timestampLast, uint48 blockTimestamp, int256 fundingRateX128, uint256 virtualPriceX128 ) internal pure returns (int256 aX128) { return fundingRateX128.mulDiv(virtualPriceX128, FixedPoint128.Q128) * int48(blockTimestamp - timestampLast); } function extrapolatedSumAX128( int256 sumAX128, uint48 timestampLast, uint48 blockTimestamp, int256 fundingRateX128, uint256 virtualPriceX128 ) internal pure returns (int256) { return sumAX128 + nextAX128(timestampLast, blockTimestamp, fundingRateX128, virtualPriceX128); } /// @notice Extrapolates (updates) the value of sumFp by adding the missing component to it using sumAGlobalX128 /// @param sumAX128 sumA value that is recorded from global at some point in time /// @param sumBX128 sumB value that is recorded from global at same point in time as sumA /// @param sumFpX128 sumFp value that is recorded from global at same point in time as sumA and sumB /// @param sumAGlobalX128 latest sumA value (taken from global), used to extrapolate the sumFp function extrapolatedSumFpX128( int256 sumAX128, int256 sumBX128, int256 sumFpX128, int256 sumAGlobalX128 ) internal pure returns (int256) { return sumFpX128 + sumBX128.mulDiv(sumAGlobalX128 - sumAX128, int256(FixedPoint128.Q128)); } /// @notice Positive bill is charged from LPs, Negative bill is rewarded to LPs /// @param sumAX128 latest value of sumA (to be taken from global state) /// @param sumFpInsideX128 latest value of sumFp inside range (to be computed using global state + tick state) /// @param sumALastX128 value of sumA when LP updated their liquidity last time /// @param sumBInsideLastX128 value of sumB inside range when LP updated their liquidity last time /// @param sumFpInsideLastX128 value of sumFp inside range when LP updated their liquidity last time /// @param liquidity amount of liquidity which was constant for LP in the time duration /// @return amount of vQuote tokens that should be charged if positive function bill( int256 sumAX128, int256 sumFpInsideX128, int256 sumALastX128, int256 sumBInsideLastX128, int256 sumFpInsideLastX128, uint256 liquidity ) internal pure returns (int256) { return (sumFpInsideX128 - extrapolatedSumFpX128(sumALastX128, sumBInsideLastX128, sumFpInsideLastX128, sumAX128)) .mulDivRoundingDown(liquidity, FixedPoint128.Q128); } /// @notice Positive bill is charged from Traders, Negative bill is rewarded to Traders /// @param sumAX128 latest value of sumA (to be taken from global state) /// @param sumALastX128 value of sumA when trader updated their netTraderPosition /// @param netTraderPosition oken amount which should be constant for time duration since sumALastX128 was recorded /// @return amount of vQuote tokens that should be charged if positive function bill( int256 sumAX128, int256 sumALastX128, int256 netTraderPosition ) internal pure returns (int256) { return netTraderPosition.mulDiv((sumAX128 - sumALastX128), int256(FixedPoint128.Q128)); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Math functions that do not check inputs or outputs /// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks library UnsafeMath { /// @notice Returns ceil(x / y) /// @dev division by 0 has unspecified behavior, and must be checked externally /// @param x The dividend /// @param y The divisor /// @return z The quotient, ceil(x / y) function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly { z := add(div(x, y), gt(mod(x, y), 0)) } } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 4999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
[{"inputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"}],"name":"IllegalSqrtPrice","type":"error"},{"inputs":[],"name":"InsufficientCollateralBalance","type":"error"},{"inputs":[{"internalType":"int256","name":"accountMarketValue","type":"int256"},{"internalType":"int256","name":"totalRequiredMargin","type":"int256"}],"name":"InvalidLiquidationAccountAboveWater","type":"error"},{"inputs":[{"internalType":"uint32","name":"poolId","type":"uint32"}],"name":"InvalidLiquidationActiveRangePresent","type":"error"},{"inputs":[{"internalType":"int256","name":"accountMarketValue","type":"int256"},{"internalType":"int256","name":"totalRequiredMargin","type":"int256"}],"name":"InvalidTransactionNotEnoughMargin","type":"error"},{"inputs":[{"internalType":"int256","name":"totalProfit","type":"int256"}],"name":"InvalidTransactionNotEnoughProfit","type":"error"},{"inputs":[{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"LPS_DeactivationFailed","type":"error"},{"inputs":[{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"LPS_IllegalTicks","type":"error"},{"inputs":[],"name":"LPS_InactiveRange","type":"error"},{"inputs":[],"name":"LP_AlreadyInitialized","type":"error"},{"inputs":[],"name":"LP_IneligibleLimitOrderRemoval","type":"error"},{"inputs":[{"internalType":"uint256","name":"y_target","type":"uint256"},{"internalType":"uint160","name":"x_lower","type":"uint160"},{"internalType":"uint160","name":"x_upper","type":"uint160"}],"name":"SolutionOutOfBounds","type":"error"},{"inputs":[],"name":"T","type":"error"},{"inputs":[{"internalType":"uint32","name":"element","type":"uint32"}],"name":"U32L8_IllegalElement","type":"error"},{"inputs":[{"internalType":"uint32","name":"element","type":"uint32"}],"name":"U32L8_NoSpaceLeftToInsert","type":"error"},{"inputs":[{"internalType":"uint48","name":"element","type":"uint48"}],"name":"U48L5_IllegalElement","type":"error"},{"inputs":[{"internalType":"uint48","name":"element","type":"uint48"}],"name":"U48L5_NoSpaceLeftToInsert","type":"error"},{"inputs":[{"internalType":"uint32","name":"poolId","type":"uint32"}],"name":"VPS_DeactivationFailed","type":"error"},{"inputs":[{"internalType":"uint32","name":"poolId","type":"uint32"}],"name":"VPS_TokenInactive","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountId","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeperAddress","type":"address"},{"indexed":false,"internalType":"int256","name":"liquidationFee","type":"int256"},{"indexed":false,"internalType":"int256","name":"keeperFee","type":"int256"},{"indexed":false,"internalType":"int256","name":"insuranceFundFee","type":"int256"},{"indexed":false,"internalType":"int256","name":"accountMarketValueFinal","type":"int256"}],"name":"LiquidityPositionsLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountId","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"collateralId","type":"uint32"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"},{"indexed":false,"internalType":"bool","name":"isSettleProfit","type":"bool"}],"name":"MarginUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountId","type":"uint256"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"ProfitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountId","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"poolId","type":"uint32"},{"indexed":false,"internalType":"int256","name":"keeperFee","type":"int256"},{"indexed":false,"internalType":"int256","name":"insuranceFundFee","type":"int256"},{"indexed":false,"internalType":"int256","name":"accountMarketValueFinal","type":"int256"}],"name":"TokenPositionLiquidated","type":"event"}]
Contract Creation Code
6080806040523461001d576158de9081620000238239308160070152f35b600080fdfe6080604052307f000000000000000000000000000000000000000000000000000000000000000014600436101561003557600080fd5b600090813560e01c80630c6d7f3414610185578063140cf61e14610177578063651524ed1461016c5780637188a1ed14610161578063998c533514610156578063a0ba77571461014b578063c27789d11461013d578063c4b22b8614610132578063d25c4a6f14610124578063df19bf0914610116578063e0448f0a14610108578063eaf39326146100fa578063ec146496146100ec5763f5a284a1146100dc575b600080fd5b6100e957506100d7611119565b80fd5b506100e957506100d7610de7565b506100e957506100d7610dcc565b506100e957506100d7610bd3565b506100e957506100d76109c8565b506100e957506100d7610926565b5050506100d7610906565b506100e957506100d7610819565b5050506100d7610679565b5050506100d7610453565b5050506100d7610431565b5050506100d76103ed565b506100e957506100d761033f565b5050506100d76101f6565b801515036100d757565b61010435906101a882610190565b565b60e435906101a882610190565b61012435906101a882610190565b61016435906101a882610190565b60031960609101126100d757600435906024356101ef81610190565b9060443590565b50610209610203366101d3565b91611ff5565b60408051928352602083019190915290f35b0390f35b6024359063ffffffff821682036100d757565b50634e487b7160e01b600052604160045260246000fd5b60a0810190811067ffffffffffffffff82111761026557604052565b61026d610232565b604052565b6040810190811067ffffffffffffffff82111761026557604052565b610100810190811067ffffffffffffffff82111761026557604052565b6060810190811067ffffffffffffffff82111761026557604052565b610120810190811067ffffffffffffffff82111761026557604052565b90601f601f19910116810190811067ffffffffffffffff82111761026557604052565b604051906101a882610249565b604051906101a8826102ab565b6001600160a01b038116036100d757565b60a435906101a882610321565b506101206003193601126100d75761035561021f565b60a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126100d7576040516102099161038f82610249565b60443582526064356103a081610321565b60208301526084356103b181610190565b604083015260a4356103c281610190565b606083015260c4356103d381610190565b60808301526103e061019a565b9160e435916004356112a2565b5060406003193601126100d757610408602435600435611cb6565b005b60031960609101126100d7576004359060243563ffffffff811681036100d7579060443590565b50602061044b60016104423661040a565b9290910161515a565b604051908152f35b50610460610203366101d3565b9081811261046a57005b6040517fc8a3942d00000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b600311156104af57565b634e487b7160e01b600052602160045260246000fd5b9060038210156104af5752565b919092608092838101936001600160a01b038096168252602093848301526040958187840152835180965260a095858785019501916000905b8282106106575750505050606091828185039101528651948584528484019285808860051b8701019901976000955b88871061054e575050505050505050505090565b90919293949596979899601f19828203018752888b51878784019163ffffffff8151168552838101518486015286810151878601528781015188860152015192878982015283518092528260c08092019401916000915b8183106105c85750505050806001929c019701970195989796949392919061053a565b919361012084966106488d6001959751908151600290810b855286830151900b8685015261060e8d80840151908601906fffffffffffffffffffffffffffffffff169052565b818e0151848f015280820151908401528d8101518e840152868101518784015260e0808201519084015261010090810151908301906104c5565b019501930190918c93926105a5565b835180518216885288015188880152958901959287019260019091019061050b565b506040806003193601126100d757600435805460601c90606881019061069e8261280a565b916106a8836122a1565b926106b5865194856102e4565b808452601f196106c4826122a1565b0160005b8181106107975750509060009060016024350160698501925b84811061070757888861021b896106fa60018b01614c69565b91909451948594856104d2565b8061076d610754610747610731610721610792968961225b565b905463ffffffff9160031b1c1690565b869063ffffffff16600052602052604060002090565b546001600160a01b031690565b61075e838b6122ff565b51906001600160a01b03169052565b61077d610731610721838761225b565b54602061078a838b6122ff565b5101526122c8565b6106e1565b60209088516107a581610272565b60008152826000818301528289010152016106c8565b8060020b036100d757565b604435906101a8826107bb565b606435906101a8826107bb565b6084359081600f0b82036100d757565b61ffff8116036100d757565b60c435906101a8826107f0565b610104359060038210156100d757565b506101806003193601126100d75761082f61021f565b6101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126100d7576108e961021b916040519061086e8261028e565b6108766107c6565b82526108806107d3565b602083015261088d6107e0565b604083015261089a610332565b60608301526108a76107fc565b60808301526108b46101aa565b60a08301526108c1610809565b60c08301526108ce6101b7565b60e08301526108db6101c5565b9161014435916004356112f2565b604080519384526020840192909252908201529081906060820190565b5060406003193601126100d757602061044b602435600160043501614608565b5060806003193601126100d75760243560043560443560643561094881610190565b6109528484611c7b565b50806109be575b61099c575b5060206bffffffffffffffffffffffff7f34e55ef9f5cf05f9a666ce27e7dcee31bb2a4e21eccaa3ea58a8358117b1547a92541692604051908152a2005b806109aa6109b09284611cb6565b82611f50565b9081811261046a575061095e565b5060008312610959565b506040806003193601126100d7576004803591602435916109e98385611e65565b8082959213610b955750600185016bffffffffffffffffffffffff86541692600093845b600860ff821610610a70575b610a548961021b8a8a610a398b610a3260058d01611542565b9084612133565b959094610a4e610a498888611619565b611675565b90611c7b565b5051938493846040919493926060820195825260208201520152565b610a7d81859a969561225b565b929063ffffffff809154600395861b1c1615610b8a57908291610aa48c979695948861225b565b905490851b1c169184610abf84610ab9611c5c565b9961581c565b94850194019460208801988b89019c5b65ffffffffffff8754168015610b4a57610b438f918f908e8e610b1b8f8f8f8f8f610b15610afb611c5c565b998a959065ffffffffffff16600052602052604060002090565b9261376e565b610b2783518251611619565b9052610b3860208301518251611619565b905201518251611619565b9052610acf565b509c909d988886610b7f97949a9c50610b79959d969950610b859850610b71928a8d6133f6565b5190896124dd565b906121d9565b95612232565b610a0d565b505083949850610a19565b92517fc2d73c750000000000000000000000000000000000000000000000000000000081529182019384525060208301919091529081906040010390fd5b5060c06003193601126100d757600435610beb61021f565b90604435610bf8816107bb565b60643592610c05846107bb565b60a4359060018401946bffffffffffffffffffffffff855416610c28838861581c565b96610c31611c5c565b92610c616001600160a01b036001610c59888a9063ffffffff16600052602052604060002090565b0154166130c4565b9660029180830b82840b13610d90579062ffffff610c9a92169060181b0160048b019065ffffffffffff16600052602052604060002090565b96610ca4886144ba565b15610d6657875490820b8160201c830b8112159283610d4f575b8315610d1e575b50505015610cf457610ce48584610ce998878660036104089e0161376e565b6133f6565b610a4e608435611675565b60046040517f11bf7ba8000000000000000000000000000000000000000000000000000000008152fd5b600883901c900b1215915081610d38575b50388080610cc5565b6001915060ff16610d48816104a5565b1438610d2f565b80935060ff8316610d5f816104a5565b1492610cbe565b60046040517fa9807f84000000000000000000000000000000000000000000000000000000008152fd5b6040517f68a08b20000000000000000000000000000000000000000000000000000000008152600292830b6004820152910b6024820152604490fd5b5060406003193601126100d757610408602435600435611a5d565b50610df13661040a565b916000926001820192610e1e610e1a6003610e0c848861581c565b015465ffffffffffff161590565b1590565b6110e057610e2c8284611e65565b95909486861315610e70576040517fc2d73c750000000000000000000000000000000000000000000000000000000081526004810187905260248101889052604490fd5b611024610fe7610fbe867f6a6d022cfd31816e7e858d405deae2fca92876abf9ddb7562057fa1e3724b527958763ffffffff966110139c610ece8f610ec8610ec1600589015461ffff9060201c1690565b61ffff1690565b9061256d565b126110d7575b610ee7610ee1838361581c565b54611675565b92610f04610ef4856125c6565b610efe8588612368565b906116d0565b816110a5575b50611084575b6bffffffffffffffffffffffff8c610fa2610f86600088610f318882612321565b9189131561105157610f72916001600160a01b03610f66610ec1610f616005610f6d96015461ffff9060501c1690565b6116b2565b9116611722565b611919565b925b546bffffffffffffffffffffffff1690565b91610f8f610307565b9687526001600160a01b03166020870152565b600060408601526001606086015260006080860152169061311e565b969050610fe06005610fd9610fd3848c611cff565b996125c6565b9201611542565b90876121b3565b979096611000610ffa610a498b8b611619565b82611c7b565b50546bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff1690565b604080518781526020810189905290810195909552921692606090a3604080519182526020820192909252f35b61107e916001600160a01b03610f66610ec16110796005610f6d96015461ffff9060501c1690565b611694565b92610f74565b9161109f90610ec8610ec1600587015461ffff9060301c1690565b91610f10565b90506110d06110c3600587015467ffffffffffffffff9060a01c1690565b67ffffffffffffffff1690565b108e610f0a565b60019250610ed4565b6040517f595138ff00000000000000000000000000000000000000000000000000000000815263ffffffff919091166004820152602490fd5b5060a06003193601126100d75760043561113161021f565b6044359160843561114181610190565b600084131561119c575063ffffffff61118661101383610f74878760687f4ba43b104605591549ff8a33304788a27f6fae3d17dbaa83e6cd68c70ad5ee9898016121e5565b60408051958652600060208701529190931693a3005b6111a584611675565b6069830190806111c586849063ffffffff16600052602052604060002090565b5410611278577f4ba43b104605591549ff8a33304788a27f6fae3d17dbaa83e6cd68c70ad5ee989363ffffffff9361123c87856110139561121983611186999063ffffffff16600052602052604060002090565b90815481811061126b575b0390559063ffffffff16600052602052604060002090565b5415611259575b15610f74576112546064358261192d565b610f74565b61126687606884016126ee565b611243565b611273611602565b611224565b60046040517fb1eae4f1000000000000000000000000000000000000000000000000000000008152fd5b91949290936112c882876080976bffffffffffffffffffffffff8754166001880161311e565b95909601516112e3575b6112da575050565b6101a89161192d565b6112ed8284611a5d565b6112d2565b94929390919460018101926bffffffffffffffffffffffff825416916113188286615884565b92611321611c5c565b9360038101908a51908160020b9060208d0151918260020b9081811361150557505091816113818e9460048f9998979562ffffff168560181b60050b019161136983886144dc565b019065ffffffffffff16600052602052604060002090565b9161138b836144ba565b1561146e575b50506113e760a0846113d3898c61141f9f978d976113bd60c0859c01516113b7816104a5565b82614483565b888c6113cd6040890151600f0b90565b93613798565b6113e086858d8a896133f6565b0151151590565b80611461575b611441575b505050506020840151935197889561140e60e0879c0151151590565b611432575b611422575b50506124f4565b90565b61142b9161192d565b3881611418565b61143c8284611a5d565b611413565b6114589361145260408a0151611675565b926145b2565b508238856113f2565b50604088015115156113ed565b93509991955061147d816144ba565b6114db5780547fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ff1660089390931b63ffffff00169290921766ffffff0000000060209a909a1b9990991698909817815589938b916113e760a0611391565b60046040517fa0bb33f9000000000000000000000000000000000000000000000000000000008152fd5b6040517f68a08b20000000000000000000000000000000000000000000000000000000008152600291820b600482015291900b6024820152604490fd5b906101a860405160e0610100820167ffffffffffffffff90838110828211176115f5575b6040528295546115ca61ffff8083168652808360101c166020870152611598818460201c16604088019061ffff169052565b61ffff603084901c821616606087015261ffff604084901c82161660808701528260501c1660a086019061ffff169052565b67ffffffffffffffff606082901c83161660c085015260a01c1691019067ffffffffffffffff169052565b6115fd610232565b611566565b50634e487b7160e01b600052601160045260246000fd5b60008112817f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038313811516611668575b81600160ff1b0383121661165c570190565b611664611602565b0190565b611670611602565b61164a565b600160ff1b8114611687575b60000390565b61168f611602565b611681565b61ffff166127108181106116a6570390565b6116ae611602565b0390565b61ffff80911690816127109103811161165c570190565b156100d757565b60001982820990828102928380841093039280840393146117195770010000000000000000000000000000000091838311156100d7570990828211900360801b910360801c1790565b50505060801c90565b906000198183098183029182808310920391808303921461177f5761271090828211156100d7577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e91940990828211900360fc1b910360041c170290565b505061271091500490565b90600019818309818302918280831092039180830392146117e857620186a090828211156100d7577f58cd20afa2f05a708ede54b48d3ae685db76b3bb83cf2cf95d4e8fb00bcbe61d940990828211900360fb1b910360051c170290565b5050620186a091500490565b6000198282099082810292838084109303928084039314611835576801000000000000000091838311156100d7570990828211900360c01b910360401c1790565b50505060401c90565b6000198282099082810292838084109303928084039314611883576c0100000000000000000000000091838311156100d7570990828211900360a01b910360601c1790565b50505060601c90565b90916000198383099280830292838086109503948086039514611906579082916118b78684116116c9565b0981806000031680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b505091506119158215156116c9565b0490565b906001600160a01b0382169182036100d757565b90600090600161193d8285611cff565b9301918080815b600860ff8216106119a3575b506119759261196763ffffffff9361196d936152ba565b90611619565b935416151590565b611987575b509081811261046a575050565b60060154908181121561199c57505b3861197a565b9050611996565b919063ffffffff906119cb6119b8858961225b565b939054600394851b1c1663ffffffff1690565b15611a555790611a0a91611a156119e5610721878b61225b565b926119f189858c61549e565b9590948a9063ffffffff16600052602052604060002090565b015460581c60ff1690565b15611a3c5791611a2b611a3192611a3794611619565b93611619565b92612232565b611944565b929394611967611a3793611a4f936152ba565b93612232565b919250611950565b9063ffffffff6003611a728360018601614608565b92015416916000821315611a9c57916101a892611a97611a9184611675565b82611b24565b611b6c565b9060008112611aab575b505050565b611ad2611acb84606985019063ffffffff16600052602052604060002090565b5491611675565b9081811115611b0d5750915b8215611aa65782611b02611b0792611afb610a496101a897611b15565b9085611b6c565b611b15565b90611b24565b905091611ade565b600160ff1b8110156100d75790565b60206bffffffffffffffffffffffff7f34e55ef9f5cf05f9a666ce27e7dcee31bb2a4e21eccaa3ea58a8358117b1547a92611b5f8582611c7b565b50541692604051908152a2565b6000831315611bce5763ffffffff611bb061101383610f74878760687f4ba43b104605591549ff8a33304788a27f6fae3d17dbaa83e6cd68c70ad5ee9898016121e5565b6040805195865260016020870152919093169390819081015b0390a3565b611bd783611675565b906069810182611bf785839063ffffffff16600052602052604060002090565b54106112785761101363ffffffff92611c4a86847f4ba43b104605591549ff8a33304788a27f6fae3d17dbaa83e6cd68c70ad5ee989761121983611bb0989063ffffffff16600052602052604060002090565b54610f745761125486606883016126ee565b60405190611c69826102ab565b60006040838281528260208201520152565b9190611c85611c5c565b50611cb26003604051611c97816102ab565b83815260006020820152600060408201529401918254611619565b9055565b906001611cc39201614608565b60008112611cce5750565b602490604051907f277ac8f70000000000000000000000000000000000000000000000000000000082526004820152fd5b9190600190611d1081838601614608565b906068850160009384925b600860ff851610611d36575b5050505061141f929350611619565b90919294611d47610721878661225b565b63ffffffff9081811615611e5d5791611967611dfd8b610b7f9488611da7611da1896069611d8a611e199c868f019063ffffffff16600052602052604060002090565b96019063ffffffff16600052602052604060002090565b54611b15565b9201549060405180927fdd86c101000000000000000000000000000000000000000000000000000000008252816001600160a01b038160209889968560a01c166004830191909163ffffffff6020820193169052565b0392165afa928315611e50575b600093611e21575b50506125a2565b929190611d1b565b611e41929350803d10611e49575b611e3981836102e4565b810190612285565b903880611e12565b503d611e2f565b611e58612294565b611e0a565b505094611d27565b9190916001611e748483611cff565b91019260008080805b87600860ff831610611eca575b5050611eaa9261196763ffffffff93611ea2936152ba565b955416151590565b611eb357509190565b60060154909281811215611ec5575090565b905090565b90929163ffffffff611ef2611edf868561225b565b929054600393841b1c1663ffffffff1690565b15611f465790611f1883926119f189611f116107218a611a0a9961225b565b80966152c7565b15611f335791611a2b611a3192611f2e94611619565b611e7d565b929394611967611f2e93611a4f936152ba565b5091925080611e8a565b9190916001611f5f8483611cff565b91019260008080805b87600860ff831610611f8c575050611eaa9261196763ffffffff93611ea2936152ba565b90929163ffffffff611fa1611edf868561225b565b15611f465790611fc783926119f189611fc06107218a611a0a9961225b565b80966155a6565b15611fe25791611a2b611a3192611fdd94611619565b611f68565b929394611967611fdd93611a4f936152ba565b90929160016120048284611cff565b92019360009081829183905b600860ff831610612033575050611eaa9261196763ffffffff93611ea2936152ba565b90929163ffffffff868a61205d61204a888361225b565b949054600395861b1c1663ffffffff1690565b156120d9578492612085611a0a95938361207d6107218c61209e9761225b565b9687916156ae565b9590948b9063ffffffff16600052602052604060002090565b156120c057916120b4611a4f926120ba94611619565b94611619565b90612010565b9394956119676120ba936120d3936152ba565b94612232565b5050509192611e8a565b6000821282600160ff1b018212811516612126575b827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018213166116a6570390565b61212e611602565b6120f8565b9061215661217c61218392959495608061217361ffff948593848b51169061178a565b67ffffffffffffffff60c08b0151168181106121ab575b50611b15565b97015116611694565b168461256d565b8360006121918396856120e3565b12156121a1575061141f916120e3565b61141f92506120e3565b90503861216d565b90611b0261217c61218392959495608061217361ffff9485938460208c0151169061178a565b8119811161165c570190565b611cb291600161220f926121f983826125db565b019063ffffffff16600052602052604060002090565b9182546121d9565b600019906001811061165c570190565b8181106116a6570390565b60ff6001911660ff811461165c570190565b50634e487b7160e01b600052603260045260246000fd5b601c909291926008841015612278575b8360031c019260021b1690565b612280612244565b61226b565b908160209103126100d7575190565b506040513d6000823e3d90fd5b60209067ffffffffffffffff81116122bb575b60051b0190565b6122c3610232565b6122b4565b600190600019811461165c570190565b6020908051156122e6570190565b611664612244565b6040908051600110156122e6570190565b6020918151811015612314575b60051b010190565b61231c612244565b61230c565b61234161141f92612363929063ffffffff16600052602052604060002090565b63ffffffff60036001600160a01b0360018401541692015460301c1690612f05565b612885565b906123716123bc565b9161238f82600983019063ffffffff16600052602052604060002090565b549263ffffffff8085169116036123a857505060201c90565b61141f9250906123b791612321565b612468565b61a4b14614801561245c575b8015612450575b156124465763ffffffff6040517fa3b1b31d00000000000000000000000000000000000000000000000000000000815260208160048160645afa908115612439575b60009161241d57501690565b612435915060203d8111611e4957611e3981836102e4565b1690565b612441612294565b612411565b63ffffffff431690565b5062066eec46146123cf565b5062066eeb46146123c8565b6001600160a01b03166401000276a3811080156124bf575b61248e578061141f916117f4565b602490604051907f5d236c450000000000000000000000000000000000000000000000000000000082526004820152fd5b5073fffd8963efd1fc6a506488495d951d5263988d26811015612480565b90610efe906124ee61141f946125c6565b92612368565b61250c90610efe610b79936124ee61141f97966125c6565b916125c6565b60016000198183600160ff1b058212600085131616612560575b827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff05136000831216166116875760000390565b612568611602565b61252c565b9190611b026125919160008512948560001461258c5761258c90612512565b611722565b9161259857565b9061141f90611675565b9190611b02612591916000851294856000146125c1576125c190612512565b6116d0565b60008113156125d25790565b61141f90611675565b63ffffffff82169081156126bc576008809260005b828110612667575b50508214612630576101a8929161260e9161225b565b90919082549060031b9163ffffffff9283811b93849216901b16911916179055565b6040517f4e99453600000000000000000000000000000000000000000000000000000000815263ffffffff84166004820152602490fd5b81612681612678610721848861225b565b63ffffffff1690565b146126b457612696612678610721838761225b565b156126a9576126a4906122c8565b6125f0565b9350389050806125f8565b505050505050565b6040517f3b16221e00000000000000000000000000000000000000000000000000000000815260006004820152602490fd5b9063ffffffff169081156126bc5790600880916000905b82821061279c575b5080820361279657506007905b820361272557505050565b80820361274c575061273a906101a89261225b565b63ffffffff82549160031b1b19169055565b8261277561273a61276d612766610721866101a89961225b565b958461225b565b94909361225b565b919082549060031b9163ffffffff9283811b93849216901b16911916179055565b9061271a565b93929190846127b1612678610721848861225b565b14612802575b6127c7612678610721838761225b565b156127de576127d5906122c8565b90919293612705565b929350909160009080156127fc576127f69150612217565b3861270d565b506127f6565b9150816127b7565b60005b60ff8116600881101561284b5763ffffffff612829838561225b565b90549060031b1c1615612845575061284090612232565b61280d565b91505090565b505050600890565b50634e487b7160e01b600052601260045260246000fd5b8015612878575b6000190490565b612880612853565b612871565b60020b6000811215612c2657806000035b620d89e88111612bfc576001600160a01b03916001821615612bd35770ffffffffffffffffffffffffffffffffff6ffffcb933bd6fad37aa2d162d1a5940015b169160028116612bb7575b60048116612b9b575b60088116612b7f575b60108116612b63575b60208116612b47575b60408116612b2b575b608090818116612b10575b6101008116612af5575b6102008116612ada575b6104008116612abf575b6108008116612aa4575b6110008116612a89575b6120008116612a6e575b6140008116612a53575b6180008116612a38575b620100008116612a1d575b620200008116612a03575b6204000081166129e9575b62080000166129ce575b506000126129c0575b63ffffffff81166129b75760ff60005b169060201c011690565b60ff60016129ad565b6129c99061286a565b61299d565b6b048a170391f7dc42444e8fa26000929302901c9190612994565b6d2216e584f5fa1ea926041bedfe98909302811c9261298a565b926e5d6af8dedb81196699c329225ee60402811c9261297f565b926f09aa508b5b7a84e1c677de54f3e99bc902811c92612974565b926f31be135f97d08fd981231505542fcfa602811c92612969565b926f70d869a156d2a1b890bb3df62baf32f702811c9261295f565b926fa9f746462d870fdf8a65dc1f90e061e502811c92612955565b926fd097f3bdfd2022b8845ad8f792aa582502811c9261294b565b926fe7159475a2c29b7443b29c7fa6e889d902811c92612941565b926ff3392b0822b70005940c7a398e4b70f302811c92612937565b926ff987a7253ac413176f2b074cf7815e5402811c9261292d565b926ffcbe86c7900a88aedcffc83b479aa3a402811c92612923565b926ffe5dee046a99a2a811c461f1969c305302811c92612919565b916fff2ea16466c96a3843ec78b326b528610260801c9161290e565b916fff973b41fa98c081472e6896dfb254c00260801c91612905565b916fffcb9843d60f6159c9db58835c9266440260801c916128fc565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c916128f3565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c916128ea565b916ffff97272373d413259a46990580e213a0260801c916128e1565b70ffffffffffffffffffffffffffffffffff7001000000000000000000000000000000006128d6565b60046040517f2bc80f3a000000000000000000000000000000000000000000000000000000008152fd5b80612896565b60405190612c39826102ab565b600282526040366020840137565b81601f820112156100d757805191612c5e836122a1565b92612c6c60405194856102e4565b808452602092838086019260051b8201019283116100d7578301905b828210612c96575050505090565b8380918351612ca481610321565b815201910190612c88565b9190916040818403126100d75780519267ffffffffffffffff938481116100d75782019381601f860112156100d757845194612cea866122a1565b90612cf860405192836102e4565b868252602096878084019160051b830101918583116100d7578801905b828210612d3457505050948301519081116100d75761141f9201612c47565b81518060060b81036100d7578152908801908801612d15565b6020908160408183019282815285518094520193019160005b828110612d74575050505090565b835163ffffffff1685529381019392810192600101612d66565b60060b9060060b9060008212827fffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000018212811516612ddc575b82667fffffffffffff018213166116a6570390565b612de4611602565b612dc7565b60060b9060060b908115612e35575b60001982147fffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000821416612e29570590565b612e31611602565b0590565b612e3d612853565b612df8565b9060060b908115612e55575b60060b0790565b612e5d612853565b612e4e565b6000199060020b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000811461165c570190565b908160e09103126100d7578051612eaa81610321565b916020820151612eb9816107bb565b916040810151612ec8816107f0565b916060820151612ed7816107f0565b916080810151612ee6816107f0565b9160a082015160ff811681036100d75760c09092015161141f81610190565b9063ffffffff811680156130bd57612f846001600160a01b0393612f3f612f2a612c2c565b94612f34866122d8565b9063ffffffff169052565b600094859182612f4e876122ee565b521693604051809381927f883bdbfd00000000000000000000000000000000000000000000000000000000835260048301612d4d565b0381865afa849181613098575b5061301957505060e0600491604051928380927f3850c7bd0000000000000000000000000000000000000000000000000000000082525afa91821561300c575b91612fda575090565b612ffb915060e03d8111613005575b612ff381836102e4565b810190612e94565b5050505050905090565b503d612fe9565b613014612294565b612fd1565b61304491925061303e613031613038613031846122ee565b5160060b90565b926122d8565b90612d8e565b9060060b61305b6130558284612de9565b60020b90565b928260060b12918261307a575b50506130715790565b61141f90612e62565b61308f92509061308991612e42565b60060b90565b15153880613068565b6130b59192503d8087833e6130ad81836102e4565b810190612caf565b509038612f91565b505061141f905b60e06001600160a01b03916004604051809481937f3850c7bd000000000000000000000000000000000000000000000000000000008352165afa908115613111575b600091612fda575090565b613119612294565b613106565b916131529493916001600160a01b03600261314984889063ffffffff16600052602052604060002090565b015416936131e1565b9091565b908160e09103126100d75760c06040519160e0830183811067ffffffffffffffff8211176131d4575b604052805183526020810151602084015260408101516040840152606081015160608401526080810151608084015260a08101516131bc81610321565b60a084015201516131cc81610321565b60c082015290565b6131dc610232565b61317f565b9495939260e0909291928051946131fb6040830151151590565b156133af576000806001600160a01b0361322260208651965b01516001600160a01b031690565b6040517f195cd92c00000000000000000000000000000000000000000000000000000000815293909912600484015260248301949094526001600160a01b039097166044820152958692606492849291165af19283156133a2575b60009361334a575b5063ffffffff7fe21eb33795bf361d3419629f8b1091aef9f4c8d525bea8dd66efbbe19d05de4d916132eb6132bd6020870151611675565b98826132cc6040890151611675565b99876132d6610314565b918c83528d60208401528d60408401526133f6565b611bc961331660c061330760a08901516001600160a01b031690565b9701516001600160a01b031690565b6040519384931696898b85929094936060926080850196855260208501526001600160a01b03809216604085015216910152565b7fe21eb33795bf361d3419629f8b1091aef9f4c8d525bea8dd66efbbe19d05de4d91935061339363ffffffff9160e03d811161339b575b61338b81836102e4565b810190613156565b939150613285565b503d613381565b6133aa612294565b61327d565b6000806001600160a01b0361322260206133c88b611675565b96613214565b6005600691939293818510156133e9575b8185040193060290565b6133f1612244565b6133df565b9361350e84600293876001600160a01b03866134258561353e989063ffffffff16600052602052604060002090565b015416917f207752419b4d795bdf5fbf6fe5dc8c9f25db48ef8c82f58a5a19724babf74a2b6134ec600184019461346c87879063ffffffff16600052602052604060002090565b604051917f7779815c000000000000000000000000000000000000000000000000000000008352602083600481845afa92831561359d575b600093613571575b50816134c96134bd8e93869561364c565b9d8e99019d8e54611619565b8d55015560408051958652602086019190915263ffffffff871694918291820190565b0390a36134f982896125db565b9063ffffffff16600052602052604060002090565b9261351e60208201518554611619565b8455604081015161353460018601918254611619565b9055518254611619565b9055805415908161355c575b50613553575050565b6101a8916135aa565b6003015465ffffffffffff161590503861354a565b6134bd9350916134c96135938e939460203d8111611e4957611e3981836102e4565b94505091906134ac565b6135a5612294565b6134a4565b60018101826135c981839063ffffffff16600052602052604060002090565b54159182159261361b575b50506135e457906101a8916126ee565b60248263ffffffff604051917fd04a5725000000000000000000000000000000000000000000000000000000008352166004820152fd5b65ffffffffffff9250600391613640919063ffffffff16600052602052604060002090565b015416151582386135d4565b6001600160a01b03610a4991602061141f946004604051809581937fa3a1e7e9000000000000000000000000000000000000000000000000000000008352165afa9182156136dc575b6000926136b8575b5080600160026136b2930154910154926120e3565b906136e9565b6136b29192506136d59060203d8111611e4957611e3981836102e4565b919061369d565b6136e4612294565b613695565b909190600160008212156137395750613703600091611675565b905b80600085121561372a575061372291610efe611b02921595611675565b911561259857565b9361372292611b0292506116d0565b613705565b600f0b7fffffffffffffffffffffffffffffffff8000000000000000000000000000000081146116875760000390565b916101a8959493916113cd6fffffffffffffffffffffffffffffffff845460381c16600f0b61373e565b95929493600094600293846137cf816137c1848a9063ffffffff16600052602052604060002090565b01546001600160a01b031690565b926137d8613c70565b508985600f0b948a86139a85898d600014613b5b57505082546040517f1dce5fcf000000000000000000000000000000000000000000000000000000008152600882901c870b600290810b600483015260209290921c90960b900b6024860152506fffffffffffffffffffffffffffffffff8716604485015260c090849060649082906000906001600160a01b03165af18015613b4e575b848860009283968491613b04575b509161389f9391613899613893889594611b15565b98611b15565b95613d75565b808251906138ac916120e3565b82528a87602084019a86868d51906138c3916120e3565b8d526138de919063ffffffff16600052602052604060002090565b600101546001600160a01b03166138f490614408565b9283898861390189611675565b9361390b90611675565b946139159661417e565b61391f908b613ead565b509860018b01918254613932908c6120e3565b906040019081519061394391611619565b90528a5460381c6fffffffffffffffffffffffffffffffff16906000149563ffffffff957f841e7440ea722264a0d4fdfb3c6be43d4c3be7d0dd5334b7a2edab9e8ca845ff97613abf5750906139ab916fffffffffffffffffffffffffffffffff1690613d50565b915b6fffffffffffffffffffffffffffffffff99838b1615613a7257613a18613a1d93948d907fffffffffffffffffff00000000000000000000000000000000ffffffffffffff76ffffffffffffffffffffffffffffffff0000000000000083549260381b169116179055565b611619565b90555b613a598854965160405193849316968860201c810b908960081c900b84604091949392606082019560020b825260020b60208201520152565b0390a360381c1615613a69575050565b6101a8916141fc565b505089547fffffffffffffffffff00000000000000000000000000000000ffffffffffffff168a556000915055600085890155600060038901556000600489015560006005890155613a20565b60009094929412613ad1575b506139ad565b92613af7613ae2613afd939561373e565b6fffffffffffffffffffffffffffffffff1690565b90613d2f565b9138613acb565b613899975061389f94508693915091613b366138939360c03d8111613b47575b613b2e81836102e4565b810190613d0f565b99919490999395505050919361387e565b503d613b24565b613b56612294565b613870565b90919560c090613beb948b896001600160a01b03613b7d613ae28b549461373e565b94604051998a96879586937fa34123a70000000000000000000000000000000000000000000000000000000085528160201c810b9160081c900b600485019160409194936fffffffffffffffffffffffffffffffff91606085019660020b855260020b602085015216910152565b0393165af18015613c63575b86938791613c21575b509161389f9391613899610a49613c1b610a498a9796611b15565b99611b15565b613c1b975061389f94508693915091613899610a49613c50610a499560c03d8111613b4757613b2e81836102e4565b9b9196909b959750505050509193613c00565b613c6b612294565b613bf7565b604051906080820182811067ffffffffffffffff821117613ca7575b60405260006060838281528260208201528260408201520152565b613caf610232565b613c8c565b91908260809103126100d7576040516080810181811067ffffffffffffffff821117613d02575b60405260608082948051845260208101516020850152604081015160408501520151910152565b613d0a610232565b613cdb565b909160c0828403126100d75761141f825193604060208501519401613cb4565b6fffffffffffffffffffffffffffffffff91821691168181106116a6570390565b906fffffffffffffffffffffffffffffffff808093169116809203811161165c570190565b939190928051908460408201928351613d8e918961403e565b9283865190613d9c91611619565b865260608301928351613daf908a614141565b613db890611b15565b9687815190613dc691611619565b90528051908160028b015560200151918260038b015551938460048b0155519889600582015554948560081c60020b9560201c60020b9663ffffffff16998a95604051948594613e47948b8b8893909796959260c0959260e086019960020b865260020b602086015260408501526060840152608083015260a08201520152565b037fb9b041b729bca1f1535dde5369cddca43b655bbc9dbd3e5878047a06e11a45c791a360408051600292830b81529290910b60208301528101919091527f8415d777c210eb5ec128f3ed52b314fc5c06eb3a464c58fa8f06a56aea5e744290606090a3565b549190613ec0600884901c60020b612885565b92613ed08160201c60020b612885565b9082916001600160a01b0392838088169516858110600014613f9357505085925b83918095828616918381168311613f86575b508284169586156100d757613f62611b0297613f5c8a876fffffffffffffffffffffffffffffffff998161141f9e169203167bffffffffffffffffffffffffffffffff0000000000000000000000008b60281b1661188c565b04611b15565b998093829311613f7b575b505003169160381c1661183e565b915091503880613f6d565b9650925084959238613f03565b8483959295161015613ef15792508092613ef1565b6001600160a01b039283831684831611614036575b7bffffffffffffffffffffffffffffffff0000000000000000000000009060601b169280808385031692169283156100d7571690613ffc82828661188c565b938215614029575b09614015575b808206151591040190565b906000198110156100d7576001019061400a565b614031612853565b614004565b909190613fbd565b61141f9261407c6140829261196760028201546136b26003840154916fffffffffffffffffffffffffffffffff6004860154955460381c16986120e3565b906120e3565b9061408d81836125a2565b91600083129182918261410a575b50506140a8575b50611675565b9060001991817f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0383138115166140fd575b81600160ff1b038312166140f0575b01386140a2565b6140f8611602565b6140e9565b614105611602565b6140da565b700100000000000000000000000000000000919250614128906125c6565b9160009209614139575b388061409b565b506001614132565b9061141f916fffffffffffffffffffffffffffffffff600582015491828410614171575b5460381c1691036116d0565b614179611602565b614165565b7f90ebe5c3d0a3bbd59fb51797eff156440d75c86a623dddef804547362e66595a9460e0949763ffffffff9493976141e76001600160a01b0394546040519b8160081c60020b8d528c602083811c60020b910152600f0b60408d015260ff60608d0191166104c5565b60808a015260a08901521660c08701521693a3565b9054906fffffffffffffffffffffffffffffffff8260381c16806143bf575065ffffffffffff6005927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000008160201c62ffffff169160101b16840b011691821561438d57909181906000905b828210614314575b5080820361430e57506004905b820361428757505050565b8082036142b0575061429c906101a8926133ce565b65ffffffffffff82549160031b1b19169055565b826142eb61429c6142e36142dc6142ca866101a8996133ce565b905465ffffffffffff9160031b1c1690565b95846133ce565b9490936133ce565b919082549060031b9165ffffffffffff9283811b93849216901b16911916179055565b9061427c565b93929190846143346143296142ca84886133ce565b65ffffffffffff1690565b14614385575b61434a6143296142ca83876133ce565b1561436157614358906122c8565b90919293614267565b9293509091600090801561437f576143799150612217565b3861426f565b50614379565b91508161433a565b6040517fc712b5e900000000000000000000000000000000000000000000000000000000815260006004820152602490fd5b82606491604051917f1346bf480000000000000000000000000000000000000000000000000000000083528060081c60020b600484015260201c60020b60248301526044820152fd5b60e06001600160a01b03916004604051809481937f3850c7bd000000000000000000000000000000000000000000000000000000008352165afa908115614476575b600091614455575090565b61446d915060e03d811161300557612ff381836102e4565b50505050505090565b61447e612294565b61444a565b9060038110156104af5760ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008354169116179055565b548060081c60020b15908115916144cf575090565b905060201c60020b151590565b65ffffffffffff821690811561438d576005809260005b82811061456e575b50508214614535576101a89291614511916133ce565b90919082549060031b9165ffffffffffff9283811b93849216901b16911916179055565b6040517f5503aeb500000000000000000000000000000000000000000000000000000000815265ffffffffffff84166004820152602490fd5b8161457f6143296142ca84886133ce565b146126b4576145946143296142ca83876133ce565b156145a7576145a2906122c8565b6144f3565b9350389050806144fb565b91613152949391604051926145c684610249565b8352600060208401526000604084015260006060840152600060808401526001600160a01b03600261314984889063ffffffff16600052602052604060002090565b6000916000905b600860ff83161061462a575b50506002015461141f91611619565b909263ffffffff61463b858561225b565b919054600392831b1c169182156146f05791611967611a4f926146ea946146758460018a019063ffffffff16600052602052604060002090565b9187856146e06146c96146888385614a17565b96905061196761469788614898565b976001600160a01b0380976146bf60029889929063ffffffff16600052602052604060002090565b015416908a614bde565b978b9063ffffffff16600052602052604060002090565b01541692016146f8565b9061460f565b94505061461b565b600093928492835b60058510614710575b5050505050565b909192939565ffffffffffff61472688876133ce565b90549060031b1c1680156148655761481a9161196761475d6148149360018a019065ffffffffffff16600052602052604060002090565b611967614781613a186147708a85613ead565b919061477b8c612468565b906125a2565b8254604080517f2a99a792000000000000000000000000000000000000000000000000000000008152600883901c600290810b600483015260209390931c90920b6024830152939160809182818e816001600160a01b0381604481010392165afa928315614858575b8b93614823575b505061480890611967611b02606085015186614141565b9381519101519161403e565b966122c8565b93929190614700565b614808929350908161484992903d10614851575b61484181836102e4565b81019061486c565b9190386147f1565b503d614837565b614860612294565b6147ea565b5095614709565b906080828203126100d75761141f91613cb4565b6001600160a01b0391821691168181106116a6570390565b6401000276a373fffd8963efd1fc6a506488495d951d5263988d2591600180821080156149d2575b61497b57805b6148ff575b506001600160a01b03808416908316141590816148ed575b5015611ec5575090565b90506148f883615895565b14386148e3565b928361491a61490e8584614880565b6001600160a01b031690565b11156149755761495261494c6149308584614880565b60011c737fffffffffffffffffffffffffffffffffffffff1690565b846149fb565b908261495d83615895565b111561496b575092806148c6565b93909250806148c6565b926148cb565b6040517ff7b6b2e9000000000000000000000000000000000000000000000000000000008152600481018390526401000276a3602482015273fffd8963efd1fc6a506488495d951d5263988d256044820152606490fd5b507ffffb12cdf108779ad387f409f1a66d1a2f5124ea64410ed5b3a4d30149805a4f82116148c0565b906001600160a01b03808093169116809203811161165c570190565b919091614a226123bc565b90614a4084600983019063ffffffff16600052602052604060002090565b9182549463ffffffff80921682871614600014614ab5575050506001015460ff8160e01c16600014614a90577bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1691508190565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169160201c90565b919450915060009160206003614adb84889063ffffffff16600052602052604060002090565b0154916024604051809481937fdd86c1010000000000000000000000000000000000000000000000000000000083528160301c16600483015260601c5afa928315614bd1575b92614ba6575b506003614b5482614b3e6123b7614b609589612321565b969063ffffffff16600052602052604060002090565b015460201c61ffff1690565b83821115614b9057614b8161ffff614b788685612227565b925b1683611722565b10614b8a579190565b80925090565b614b8161ffff614ba08487612227565b92614b7a565b614b60919250614b54614bc860039260203d8111611e4957611e3981836102e4565b93925050614b27565b614bd9612294565b614b21565b9061141f92614bf16119679284546125a2565b9261364c565b90614c01826122a1565b604090614c10825191826102e4565b838152601f19614c2082956122a1565b019160005b838110614c325750505050565b6020908251614c4081610249565b600081528260008183015260008583015260606000818401526080830152828601015201614c25565b6002918282015492614c7a8361280a565b90614c8482614bf7565b9360005b838110614c955750505050565b614c9f818361225b565b905460039190911b1c63ffffffff16614cb882886122ff565b5163ffffffff9091169052600180830190614cd3838561225b565b905463ffffffff60039290921b1c16600090815260208390526040902054614cfb848a6122ff565b5160200152614d0a838561225b565b905463ffffffff60039290921b1c1660009081526020839052604090200154614d3383896122ff565b516040015283614d43838561225b565b905463ffffffff60039290921b1c1660009081526020839052604090200154614d6c83896122ff565b5160600152614d7b828461225b565b905460039190911b1c63ffffffff16614da3919063ffffffff16600052602052604060002090565b600301614daf90614e68565b614db982886122ff565b5160800152614dc7906122c8565b614c88565b90614dd6826122a1565b604090614de5825191826102e4565b838152601f19614df582956122a1565b019160005b838110614e075750505050565b6020908251614e15816102c7565b60008152826000818301526000858301526000606083015260006080830152600060a0830152600060c0830152600060e08301526000610100830152828601015201614dfa565b60038210156104af5752565b90614e7282615115565b614e7b81614dcc565b9260005b828110614e8b57505050565b600180830190614e9b83856133ce565b905465ffffffffffff60039290921b1c1660009081526020839052604090205460ff16614ec884896122ff565b516101000190614ed791614e5c565b614ee183856133ce565b905465ffffffffffff60039290921b1c1660009081526020839052604090205460081c60020b614f1184896122ff565b5190614f1f919060020b9052565b614f2983856133ce565b905465ffffffffffff60039290921b1c1660009081526020839052604090205460201c60020b614f5984896122ff565b5160200190614f6a919060020b9052565b614f7483856133ce565b905465ffffffffffff60039290921b1c1660009081526020839052604090205460381c6fffffffffffffffffffffffffffffffff16614fb384896122ff565b5160400190614fd391906fffffffffffffffffffffffffffffffff169052565b614fdd83856133ce565b905465ffffffffffff60039290921b1c166000908152602083905260409020015461500883886122ff565b516060015261501782846133ce565b905465ffffffffffff60039290921b1c1660009081526020829052604090206002015461504483886122ff565b516080015261505382846133ce565b9054600391821b1c65ffffffffffff166000908152602083905260409020015461507d83886122ff565b5160a0015261508c82846133ce565b905465ffffffffffff60039290921b1c166000908152602082905260409020600401546150b983886122ff565b5160c001526150c882846133ce565b905460039190911b1c65ffffffffffff166150f4919065ffffffffffff16600052602052604060002090565b6005015461510282876122ff565b5160e00152615110906122c8565b614e7f565b60005b60ff811660058110156151525765ffffffffffff61513683856133ce565b90549060031b1c1615612845575061514d90612232565b615118565b505050600590565b929192600860ff61516b8484615263565b161461525b57906151908160018094019063ffffffff16600052602052604060002090565b82810154916151c86001600160a01b03856151c06003948587019a9063ffffffff16600052602052604060002090565b015416614408565b6000946151d488615115565b926004879501985b8486106151f45750505050505061141f929350611619565b90919293949661524b615251916119676152368d65ffffffffffff6152198e896133ce565b9054908a1b1c1665ffffffffffff16600052602052604060002090565b876152418a83613ead565b50910154906120e3565b976122c8565b94939291906151dc565b506000925050565b9060005b600860ff82161061527a57505050600890565b615284818461225b565b905463ffffffff908185169260031b1c1614612845576152a390612232565b615267565b60008113156152b45790565b50600090565b9080821315611ec5575090565b929091926152e98360018093019063ffffffff16600052602052604060002090565b6152f38486614a17565b91905061ffff61530283614898565b9661532060039788929063ffffffff16600052602052604060002090565b015460101c1692815495808301976000918291600486016001600160a01b03808316915b6005861061539a575b50505050505061141f9596975061538f8561538a6153858796613a1861538a996153959861537d61538599611b15565b9301546125a2565b6152a8565b61256d565b97611675565b6125a2565b90919293949565ffffffffffff6153b28f89906133ce565b905490881b1c1680156154975761547191610b79876153e661481494879065ffffffffffff16600052602052604060002090565b8054908d61545160026154328d8d6154116154058960081c860b612885565b948960201c900b612885565b98818a1683116154905780925b85831610615487578190925b1691166117f4565b956fffffffffffffffffffffffffffffffff60009660381c1691613fa8565b91015491821261547b57908161546c826125c19410156157b0565b612227565b9493929190615344565b610b796125c192612512565b5080849261542a565b899261541e565b509561534d565b929091926154c08360018093019063ffffffff16600052602052604060002090565b6154ca8486614a17565b91905061ffff6154d983614898565b966154f760039788929063ffffffff16600052602052604060002090565b01541692815495808301976000918291600486016001600160a01b03808316915b600586106155505750505050505061141f9596975061538f8561538a6153858796613a1861538a996153959861537d61538599611b15565b90919293949565ffffffffffff6155688f89906133ce565b905490881b1c1680156154975761559c91610b79876153e661481494879065ffffffffffff16600052602052604060002090565b9493929190615518565b929091926155c88360018093019063ffffffff16600052602052604060002090565b6155d28486614a17565b91905061ffff6155e183614898565b966155ff60039788929063ffffffff16600052602052604060002090565b01541692815495808301976000918291600486016001600160a01b03808316915b600586106156585750505050505061141f9596975061538f8561538a6153858796613a1861538a996153959861537d61538599611b15565b90919293949565ffffffffffff6156708f89906133ce565b905490881b1c168015615497576156a491610b79876153e661481494879065ffffffffffff16600052602052604060002090565b9493929190615620565b9293916156ef6156d28260018097019063ffffffff16600052602052604060002090565b926156dd8388614a17565b9390506156e984614898565b976157cd565b918054946003808301976000918291600486016001600160a01b03808316915b60058610615753575b50505050505061141f9596975061538f61ffff61574a6153858796613a1861538a996153959861537d61538599611b15565b9616809661256d565b90919293949565ffffffffffff61576b8f89906133ce565b905490881b1c1680156157a95761579f91610b79876153e661481494879065ffffffffffff16600052602052604060002090565b949392919061570f565b5095615718565b156157b757565b634e487b7160e01b600052600160045260246000fd5b60039161ffff936000146157fa576157f4919063ffffffff16600052602052604060002090565b01541690565b615813919063ffffffff16600052602052604060002090565b015460101c1690565b600860ff61582a8484615263565b161461584d5790600161141f92019063ffffffff16600052602052604060002090565b60248263ffffffff604051917f8bff134b000000000000000000000000000000000000000000000000000000008352166004820152fd5b90600161141f926121f983826125db565b6001600160a01b0361141f9116806117f456fea2646970667358221220e2f332f9dcc4d7a1b2c879eba7eb141499a511caf098179ba6b07afd7a5f922464736f6c634300080e0033
Deployed Bytecode
0x6080604052307f0000000000000000000000003fab135e76c5c0e85a60bf50f100c8cd912f7e5614600436101561003557600080fd5b600090813560e01c80630c6d7f3414610185578063140cf61e14610177578063651524ed1461016c5780637188a1ed14610161578063998c533514610156578063a0ba77571461014b578063c27789d11461013d578063c4b22b8614610132578063d25c4a6f14610124578063df19bf0914610116578063e0448f0a14610108578063eaf39326146100fa578063ec146496146100ec5763f5a284a1146100dc575b600080fd5b6100e957506100d7611119565b80fd5b506100e957506100d7610de7565b506100e957506100d7610dcc565b506100e957506100d7610bd3565b506100e957506100d76109c8565b506100e957506100d7610926565b5050506100d7610906565b506100e957506100d7610819565b5050506100d7610679565b5050506100d7610453565b5050506100d7610431565b5050506100d76103ed565b506100e957506100d761033f565b5050506100d76101f6565b801515036100d757565b61010435906101a882610190565b565b60e435906101a882610190565b61012435906101a882610190565b61016435906101a882610190565b60031960609101126100d757600435906024356101ef81610190565b9060443590565b50610209610203366101d3565b91611ff5565b60408051928352602083019190915290f35b0390f35b6024359063ffffffff821682036100d757565b50634e487b7160e01b600052604160045260246000fd5b60a0810190811067ffffffffffffffff82111761026557604052565b61026d610232565b604052565b6040810190811067ffffffffffffffff82111761026557604052565b610100810190811067ffffffffffffffff82111761026557604052565b6060810190811067ffffffffffffffff82111761026557604052565b610120810190811067ffffffffffffffff82111761026557604052565b90601f601f19910116810190811067ffffffffffffffff82111761026557604052565b604051906101a882610249565b604051906101a8826102ab565b6001600160a01b038116036100d757565b60a435906101a882610321565b506101206003193601126100d75761035561021f565b60a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126100d7576040516102099161038f82610249565b60443582526064356103a081610321565b60208301526084356103b181610190565b604083015260a4356103c281610190565b606083015260c4356103d381610190565b60808301526103e061019a565b9160e435916004356112a2565b5060406003193601126100d757610408602435600435611cb6565b005b60031960609101126100d7576004359060243563ffffffff811681036100d7579060443590565b50602061044b60016104423661040a565b9290910161515a565b604051908152f35b50610460610203366101d3565b9081811261046a57005b6040517fc8a3942d00000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b600311156104af57565b634e487b7160e01b600052602160045260246000fd5b9060038210156104af5752565b919092608092838101936001600160a01b038096168252602093848301526040958187840152835180965260a095858785019501916000905b8282106106575750505050606091828185039101528651948584528484019285808860051b8701019901976000955b88871061054e575050505050505050505090565b90919293949596979899601f19828203018752888b51878784019163ffffffff8151168552838101518486015286810151878601528781015188860152015192878982015283518092528260c08092019401916000915b8183106105c85750505050806001929c019701970195989796949392919061053a565b919361012084966106488d6001959751908151600290810b855286830151900b8685015261060e8d80840151908601906fffffffffffffffffffffffffffffffff169052565b818e0151848f015280820151908401528d8101518e840152868101518784015260e0808201519084015261010090810151908301906104c5565b019501930190918c93926105a5565b835180518216885288015188880152958901959287019260019091019061050b565b506040806003193601126100d757600435805460601c90606881019061069e8261280a565b916106a8836122a1565b926106b5865194856102e4565b808452601f196106c4826122a1565b0160005b8181106107975750509060009060016024350160698501925b84811061070757888861021b896106fa60018b01614c69565b91909451948594856104d2565b8061076d610754610747610731610721610792968961225b565b905463ffffffff9160031b1c1690565b869063ffffffff16600052602052604060002090565b546001600160a01b031690565b61075e838b6122ff565b51906001600160a01b03169052565b61077d610731610721838761225b565b54602061078a838b6122ff565b5101526122c8565b6106e1565b60209088516107a581610272565b60008152826000818301528289010152016106c8565b8060020b036100d757565b604435906101a8826107bb565b606435906101a8826107bb565b6084359081600f0b82036100d757565b61ffff8116036100d757565b60c435906101a8826107f0565b610104359060038210156100d757565b506101806003193601126100d75761082f61021f565b6101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126100d7576108e961021b916040519061086e8261028e565b6108766107c6565b82526108806107d3565b602083015261088d6107e0565b604083015261089a610332565b60608301526108a76107fc565b60808301526108b46101aa565b60a08301526108c1610809565b60c08301526108ce6101b7565b60e08301526108db6101c5565b9161014435916004356112f2565b604080519384526020840192909252908201529081906060820190565b5060406003193601126100d757602061044b602435600160043501614608565b5060806003193601126100d75760243560043560443560643561094881610190565b6109528484611c7b565b50806109be575b61099c575b5060206bffffffffffffffffffffffff7f34e55ef9f5cf05f9a666ce27e7dcee31bb2a4e21eccaa3ea58a8358117b1547a92541692604051908152a2005b806109aa6109b09284611cb6565b82611f50565b9081811261046a575061095e565b5060008312610959565b506040806003193601126100d7576004803591602435916109e98385611e65565b8082959213610b955750600185016bffffffffffffffffffffffff86541692600093845b600860ff821610610a70575b610a548961021b8a8a610a398b610a3260058d01611542565b9084612133565b959094610a4e610a498888611619565b611675565b90611c7b565b5051938493846040919493926060820195825260208201520152565b610a7d81859a969561225b565b929063ffffffff809154600395861b1c1615610b8a57908291610aa48c979695948861225b565b905490851b1c169184610abf84610ab9611c5c565b9961581c565b94850194019460208801988b89019c5b65ffffffffffff8754168015610b4a57610b438f918f908e8e610b1b8f8f8f8f8f610b15610afb611c5c565b998a959065ffffffffffff16600052602052604060002090565b9261376e565b610b2783518251611619565b9052610b3860208301518251611619565b905201518251611619565b9052610acf565b509c909d988886610b7f97949a9c50610b79959d969950610b859850610b71928a8d6133f6565b5190896124dd565b906121d9565b95612232565b610a0d565b505083949850610a19565b92517fc2d73c750000000000000000000000000000000000000000000000000000000081529182019384525060208301919091529081906040010390fd5b5060c06003193601126100d757600435610beb61021f565b90604435610bf8816107bb565b60643592610c05846107bb565b60a4359060018401946bffffffffffffffffffffffff855416610c28838861581c565b96610c31611c5c565b92610c616001600160a01b036001610c59888a9063ffffffff16600052602052604060002090565b0154166130c4565b9660029180830b82840b13610d90579062ffffff610c9a92169060181b0160048b019065ffffffffffff16600052602052604060002090565b96610ca4886144ba565b15610d6657875490820b8160201c830b8112159283610d4f575b8315610d1e575b50505015610cf457610ce48584610ce998878660036104089e0161376e565b6133f6565b610a4e608435611675565b60046040517f11bf7ba8000000000000000000000000000000000000000000000000000000008152fd5b600883901c900b1215915081610d38575b50388080610cc5565b6001915060ff16610d48816104a5565b1438610d2f565b80935060ff8316610d5f816104a5565b1492610cbe565b60046040517fa9807f84000000000000000000000000000000000000000000000000000000008152fd5b6040517f68a08b20000000000000000000000000000000000000000000000000000000008152600292830b6004820152910b6024820152604490fd5b5060406003193601126100d757610408602435600435611a5d565b50610df13661040a565b916000926001820192610e1e610e1a6003610e0c848861581c565b015465ffffffffffff161590565b1590565b6110e057610e2c8284611e65565b95909486861315610e70576040517fc2d73c750000000000000000000000000000000000000000000000000000000081526004810187905260248101889052604490fd5b611024610fe7610fbe867f6a6d022cfd31816e7e858d405deae2fca92876abf9ddb7562057fa1e3724b527958763ffffffff966110139c610ece8f610ec8610ec1600589015461ffff9060201c1690565b61ffff1690565b9061256d565b126110d7575b610ee7610ee1838361581c565b54611675565b92610f04610ef4856125c6565b610efe8588612368565b906116d0565b816110a5575b50611084575b6bffffffffffffffffffffffff8c610fa2610f86600088610f318882612321565b9189131561105157610f72916001600160a01b03610f66610ec1610f616005610f6d96015461ffff9060501c1690565b6116b2565b9116611722565b611919565b925b546bffffffffffffffffffffffff1690565b91610f8f610307565b9687526001600160a01b03166020870152565b600060408601526001606086015260006080860152169061311e565b969050610fe06005610fd9610fd3848c611cff565b996125c6565b9201611542565b90876121b3565b979096611000610ffa610a498b8b611619565b82611c7b565b50546bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff1690565b604080518781526020810189905290810195909552921692606090a3604080519182526020820192909252f35b61107e916001600160a01b03610f66610ec16110796005610f6d96015461ffff9060501c1690565b611694565b92610f74565b9161109f90610ec8610ec1600587015461ffff9060301c1690565b91610f10565b90506110d06110c3600587015467ffffffffffffffff9060a01c1690565b67ffffffffffffffff1690565b108e610f0a565b60019250610ed4565b6040517f595138ff00000000000000000000000000000000000000000000000000000000815263ffffffff919091166004820152602490fd5b5060a06003193601126100d75760043561113161021f565b6044359160843561114181610190565b600084131561119c575063ffffffff61118661101383610f74878760687f4ba43b104605591549ff8a33304788a27f6fae3d17dbaa83e6cd68c70ad5ee9898016121e5565b60408051958652600060208701529190931693a3005b6111a584611675565b6069830190806111c586849063ffffffff16600052602052604060002090565b5410611278577f4ba43b104605591549ff8a33304788a27f6fae3d17dbaa83e6cd68c70ad5ee989363ffffffff9361123c87856110139561121983611186999063ffffffff16600052602052604060002090565b90815481811061126b575b0390559063ffffffff16600052602052604060002090565b5415611259575b15610f74576112546064358261192d565b610f74565b61126687606884016126ee565b611243565b611273611602565b611224565b60046040517fb1eae4f1000000000000000000000000000000000000000000000000000000008152fd5b91949290936112c882876080976bffffffffffffffffffffffff8754166001880161311e565b95909601516112e3575b6112da575050565b6101a89161192d565b6112ed8284611a5d565b6112d2565b94929390919460018101926bffffffffffffffffffffffff825416916113188286615884565b92611321611c5c565b9360038101908a51908160020b9060208d0151918260020b9081811361150557505091816113818e9460048f9998979562ffffff168560181b60050b019161136983886144dc565b019065ffffffffffff16600052602052604060002090565b9161138b836144ba565b1561146e575b50506113e760a0846113d3898c61141f9f978d976113bd60c0859c01516113b7816104a5565b82614483565b888c6113cd6040890151600f0b90565b93613798565b6113e086858d8a896133f6565b0151151590565b80611461575b611441575b505050506020840151935197889561140e60e0879c0151151590565b611432575b611422575b50506124f4565b90565b61142b9161192d565b3881611418565b61143c8284611a5d565b611413565b6114589361145260408a0151611675565b926145b2565b508238856113f2565b50604088015115156113ed565b93509991955061147d816144ba565b6114db5780547fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ff1660089390931b63ffffff00169290921766ffffff0000000060209a909a1b9990991698909817815589938b916113e760a0611391565b60046040517fa0bb33f9000000000000000000000000000000000000000000000000000000008152fd5b6040517f68a08b20000000000000000000000000000000000000000000000000000000008152600291820b600482015291900b6024820152604490fd5b906101a860405160e0610100820167ffffffffffffffff90838110828211176115f5575b6040528295546115ca61ffff8083168652808360101c166020870152611598818460201c16604088019061ffff169052565b61ffff603084901c821616606087015261ffff604084901c82161660808701528260501c1660a086019061ffff169052565b67ffffffffffffffff606082901c83161660c085015260a01c1691019067ffffffffffffffff169052565b6115fd610232565b611566565b50634e487b7160e01b600052601160045260246000fd5b60008112817f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038313811516611668575b81600160ff1b0383121661165c570190565b611664611602565b0190565b611670611602565b61164a565b600160ff1b8114611687575b60000390565b61168f611602565b611681565b61ffff166127108181106116a6570390565b6116ae611602565b0390565b61ffff80911690816127109103811161165c570190565b156100d757565b60001982820990828102928380841093039280840393146117195770010000000000000000000000000000000091838311156100d7570990828211900360801b910360801c1790565b50505060801c90565b906000198183098183029182808310920391808303921461177f5761271090828211156100d7577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e91940990828211900360fc1b910360041c170290565b505061271091500490565b90600019818309818302918280831092039180830392146117e857620186a090828211156100d7577f58cd20afa2f05a708ede54b48d3ae685db76b3bb83cf2cf95d4e8fb00bcbe61d940990828211900360fb1b910360051c170290565b5050620186a091500490565b6000198282099082810292838084109303928084039314611835576801000000000000000091838311156100d7570990828211900360c01b910360401c1790565b50505060401c90565b6000198282099082810292838084109303928084039314611883576c0100000000000000000000000091838311156100d7570990828211900360a01b910360601c1790565b50505060601c90565b90916000198383099280830292838086109503948086039514611906579082916118b78684116116c9565b0981806000031680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b505091506119158215156116c9565b0490565b906001600160a01b0382169182036100d757565b90600090600161193d8285611cff565b9301918080815b600860ff8216106119a3575b506119759261196763ffffffff9361196d936152ba565b90611619565b935416151590565b611987575b509081811261046a575050565b60060154908181121561199c57505b3861197a565b9050611996565b919063ffffffff906119cb6119b8858961225b565b939054600394851b1c1663ffffffff1690565b15611a555790611a0a91611a156119e5610721878b61225b565b926119f189858c61549e565b9590948a9063ffffffff16600052602052604060002090565b015460581c60ff1690565b15611a3c5791611a2b611a3192611a3794611619565b93611619565b92612232565b611944565b929394611967611a3793611a4f936152ba565b93612232565b919250611950565b9063ffffffff6003611a728360018601614608565b92015416916000821315611a9c57916101a892611a97611a9184611675565b82611b24565b611b6c565b9060008112611aab575b505050565b611ad2611acb84606985019063ffffffff16600052602052604060002090565b5491611675565b9081811115611b0d5750915b8215611aa65782611b02611b0792611afb610a496101a897611b15565b9085611b6c565b611b15565b90611b24565b905091611ade565b600160ff1b8110156100d75790565b60206bffffffffffffffffffffffff7f34e55ef9f5cf05f9a666ce27e7dcee31bb2a4e21eccaa3ea58a8358117b1547a92611b5f8582611c7b565b50541692604051908152a2565b6000831315611bce5763ffffffff611bb061101383610f74878760687f4ba43b104605591549ff8a33304788a27f6fae3d17dbaa83e6cd68c70ad5ee9898016121e5565b6040805195865260016020870152919093169390819081015b0390a3565b611bd783611675565b906069810182611bf785839063ffffffff16600052602052604060002090565b54106112785761101363ffffffff92611c4a86847f4ba43b104605591549ff8a33304788a27f6fae3d17dbaa83e6cd68c70ad5ee989761121983611bb0989063ffffffff16600052602052604060002090565b54610f745761125486606883016126ee565b60405190611c69826102ab565b60006040838281528260208201520152565b9190611c85611c5c565b50611cb26003604051611c97816102ab565b83815260006020820152600060408201529401918254611619565b9055565b906001611cc39201614608565b60008112611cce5750565b602490604051907f277ac8f70000000000000000000000000000000000000000000000000000000082526004820152fd5b9190600190611d1081838601614608565b906068850160009384925b600860ff851610611d36575b5050505061141f929350611619565b90919294611d47610721878661225b565b63ffffffff9081811615611e5d5791611967611dfd8b610b7f9488611da7611da1896069611d8a611e199c868f019063ffffffff16600052602052604060002090565b96019063ffffffff16600052602052604060002090565b54611b15565b9201549060405180927fdd86c101000000000000000000000000000000000000000000000000000000008252816001600160a01b038160209889968560a01c166004830191909163ffffffff6020820193169052565b0392165afa928315611e50575b600093611e21575b50506125a2565b929190611d1b565b611e41929350803d10611e49575b611e3981836102e4565b810190612285565b903880611e12565b503d611e2f565b611e58612294565b611e0a565b505094611d27565b9190916001611e748483611cff565b91019260008080805b87600860ff831610611eca575b5050611eaa9261196763ffffffff93611ea2936152ba565b955416151590565b611eb357509190565b60060154909281811215611ec5575090565b905090565b90929163ffffffff611ef2611edf868561225b565b929054600393841b1c1663ffffffff1690565b15611f465790611f1883926119f189611f116107218a611a0a9961225b565b80966152c7565b15611f335791611a2b611a3192611f2e94611619565b611e7d565b929394611967611f2e93611a4f936152ba565b5091925080611e8a565b9190916001611f5f8483611cff565b91019260008080805b87600860ff831610611f8c575050611eaa9261196763ffffffff93611ea2936152ba565b90929163ffffffff611fa1611edf868561225b565b15611f465790611fc783926119f189611fc06107218a611a0a9961225b565b80966155a6565b15611fe25791611a2b611a3192611fdd94611619565b611f68565b929394611967611fdd93611a4f936152ba565b90929160016120048284611cff565b92019360009081829183905b600860ff831610612033575050611eaa9261196763ffffffff93611ea2936152ba565b90929163ffffffff868a61205d61204a888361225b565b949054600395861b1c1663ffffffff1690565b156120d9578492612085611a0a95938361207d6107218c61209e9761225b565b9687916156ae565b9590948b9063ffffffff16600052602052604060002090565b156120c057916120b4611a4f926120ba94611619565b94611619565b90612010565b9394956119676120ba936120d3936152ba565b94612232565b5050509192611e8a565b6000821282600160ff1b018212811516612126575b827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018213166116a6570390565b61212e611602565b6120f8565b9061215661217c61218392959495608061217361ffff948593848b51169061178a565b67ffffffffffffffff60c08b0151168181106121ab575b50611b15565b97015116611694565b168461256d565b8360006121918396856120e3565b12156121a1575061141f916120e3565b61141f92506120e3565b90503861216d565b90611b0261217c61218392959495608061217361ffff9485938460208c0151169061178a565b8119811161165c570190565b611cb291600161220f926121f983826125db565b019063ffffffff16600052602052604060002090565b9182546121d9565b600019906001811061165c570190565b8181106116a6570390565b60ff6001911660ff811461165c570190565b50634e487b7160e01b600052603260045260246000fd5b601c909291926008841015612278575b8360031c019260021b1690565b612280612244565b61226b565b908160209103126100d7575190565b506040513d6000823e3d90fd5b60209067ffffffffffffffff81116122bb575b60051b0190565b6122c3610232565b6122b4565b600190600019811461165c570190565b6020908051156122e6570190565b611664612244565b6040908051600110156122e6570190565b6020918151811015612314575b60051b010190565b61231c612244565b61230c565b61234161141f92612363929063ffffffff16600052602052604060002090565b63ffffffff60036001600160a01b0360018401541692015460301c1690612f05565b612885565b906123716123bc565b9161238f82600983019063ffffffff16600052602052604060002090565b549263ffffffff8085169116036123a857505060201c90565b61141f9250906123b791612321565b612468565b61a4b14614801561245c575b8015612450575b156124465763ffffffff6040517fa3b1b31d00000000000000000000000000000000000000000000000000000000815260208160048160645afa908115612439575b60009161241d57501690565b612435915060203d8111611e4957611e3981836102e4565b1690565b612441612294565b612411565b63ffffffff431690565b5062066eec46146123cf565b5062066eeb46146123c8565b6001600160a01b03166401000276a3811080156124bf575b61248e578061141f916117f4565b602490604051907f5d236c450000000000000000000000000000000000000000000000000000000082526004820152fd5b5073fffd8963efd1fc6a506488495d951d5263988d26811015612480565b90610efe906124ee61141f946125c6565b92612368565b61250c90610efe610b79936124ee61141f97966125c6565b916125c6565b60016000198183600160ff1b058212600085131616612560575b827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff05136000831216166116875760000390565b612568611602565b61252c565b9190611b026125919160008512948560001461258c5761258c90612512565b611722565b9161259857565b9061141f90611675565b9190611b02612591916000851294856000146125c1576125c190612512565b6116d0565b60008113156125d25790565b61141f90611675565b63ffffffff82169081156126bc576008809260005b828110612667575b50508214612630576101a8929161260e9161225b565b90919082549060031b9163ffffffff9283811b93849216901b16911916179055565b6040517f4e99453600000000000000000000000000000000000000000000000000000000815263ffffffff84166004820152602490fd5b81612681612678610721848861225b565b63ffffffff1690565b146126b457612696612678610721838761225b565b156126a9576126a4906122c8565b6125f0565b9350389050806125f8565b505050505050565b6040517f3b16221e00000000000000000000000000000000000000000000000000000000815260006004820152602490fd5b9063ffffffff169081156126bc5790600880916000905b82821061279c575b5080820361279657506007905b820361272557505050565b80820361274c575061273a906101a89261225b565b63ffffffff82549160031b1b19169055565b8261277561273a61276d612766610721866101a89961225b565b958461225b565b94909361225b565b919082549060031b9163ffffffff9283811b93849216901b16911916179055565b9061271a565b93929190846127b1612678610721848861225b565b14612802575b6127c7612678610721838761225b565b156127de576127d5906122c8565b90919293612705565b929350909160009080156127fc576127f69150612217565b3861270d565b506127f6565b9150816127b7565b60005b60ff8116600881101561284b5763ffffffff612829838561225b565b90549060031b1c1615612845575061284090612232565b61280d565b91505090565b505050600890565b50634e487b7160e01b600052601260045260246000fd5b8015612878575b6000190490565b612880612853565b612871565b60020b6000811215612c2657806000035b620d89e88111612bfc576001600160a01b03916001821615612bd35770ffffffffffffffffffffffffffffffffff6ffffcb933bd6fad37aa2d162d1a5940015b169160028116612bb7575b60048116612b9b575b60088116612b7f575b60108116612b63575b60208116612b47575b60408116612b2b575b608090818116612b10575b6101008116612af5575b6102008116612ada575b6104008116612abf575b6108008116612aa4575b6110008116612a89575b6120008116612a6e575b6140008116612a53575b6180008116612a38575b620100008116612a1d575b620200008116612a03575b6204000081166129e9575b62080000166129ce575b506000126129c0575b63ffffffff81166129b75760ff60005b169060201c011690565b60ff60016129ad565b6129c99061286a565b61299d565b6b048a170391f7dc42444e8fa26000929302901c9190612994565b6d2216e584f5fa1ea926041bedfe98909302811c9261298a565b926e5d6af8dedb81196699c329225ee60402811c9261297f565b926f09aa508b5b7a84e1c677de54f3e99bc902811c92612974565b926f31be135f97d08fd981231505542fcfa602811c92612969565b926f70d869a156d2a1b890bb3df62baf32f702811c9261295f565b926fa9f746462d870fdf8a65dc1f90e061e502811c92612955565b926fd097f3bdfd2022b8845ad8f792aa582502811c9261294b565b926fe7159475a2c29b7443b29c7fa6e889d902811c92612941565b926ff3392b0822b70005940c7a398e4b70f302811c92612937565b926ff987a7253ac413176f2b074cf7815e5402811c9261292d565b926ffcbe86c7900a88aedcffc83b479aa3a402811c92612923565b926ffe5dee046a99a2a811c461f1969c305302811c92612919565b916fff2ea16466c96a3843ec78b326b528610260801c9161290e565b916fff973b41fa98c081472e6896dfb254c00260801c91612905565b916fffcb9843d60f6159c9db58835c9266440260801c916128fc565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c916128f3565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c916128ea565b916ffff97272373d413259a46990580e213a0260801c916128e1565b70ffffffffffffffffffffffffffffffffff7001000000000000000000000000000000006128d6565b60046040517f2bc80f3a000000000000000000000000000000000000000000000000000000008152fd5b80612896565b60405190612c39826102ab565b600282526040366020840137565b81601f820112156100d757805191612c5e836122a1565b92612c6c60405194856102e4565b808452602092838086019260051b8201019283116100d7578301905b828210612c96575050505090565b8380918351612ca481610321565b815201910190612c88565b9190916040818403126100d75780519267ffffffffffffffff938481116100d75782019381601f860112156100d757845194612cea866122a1565b90612cf860405192836102e4565b868252602096878084019160051b830101918583116100d7578801905b828210612d3457505050948301519081116100d75761141f9201612c47565b81518060060b81036100d7578152908801908801612d15565b6020908160408183019282815285518094520193019160005b828110612d74575050505090565b835163ffffffff1685529381019392810192600101612d66565b60060b9060060b9060008212827fffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000018212811516612ddc575b82667fffffffffffff018213166116a6570390565b612de4611602565b612dc7565b60060b9060060b908115612e35575b60001982147fffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000821416612e29570590565b612e31611602565b0590565b612e3d612853565b612df8565b9060060b908115612e55575b60060b0790565b612e5d612853565b612e4e565b6000199060020b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000811461165c570190565b908160e09103126100d7578051612eaa81610321565b916020820151612eb9816107bb565b916040810151612ec8816107f0565b916060820151612ed7816107f0565b916080810151612ee6816107f0565b9160a082015160ff811681036100d75760c09092015161141f81610190565b9063ffffffff811680156130bd57612f846001600160a01b0393612f3f612f2a612c2c565b94612f34866122d8565b9063ffffffff169052565b600094859182612f4e876122ee565b521693604051809381927f883bdbfd00000000000000000000000000000000000000000000000000000000835260048301612d4d565b0381865afa849181613098575b5061301957505060e0600491604051928380927f3850c7bd0000000000000000000000000000000000000000000000000000000082525afa91821561300c575b91612fda575090565b612ffb915060e03d8111613005575b612ff381836102e4565b810190612e94565b5050505050905090565b503d612fe9565b613014612294565b612fd1565b61304491925061303e613031613038613031846122ee565b5160060b90565b926122d8565b90612d8e565b9060060b61305b6130558284612de9565b60020b90565b928260060b12918261307a575b50506130715790565b61141f90612e62565b61308f92509061308991612e42565b60060b90565b15153880613068565b6130b59192503d8087833e6130ad81836102e4565b810190612caf565b509038612f91565b505061141f905b60e06001600160a01b03916004604051809481937f3850c7bd000000000000000000000000000000000000000000000000000000008352165afa908115613111575b600091612fda575090565b613119612294565b613106565b916131529493916001600160a01b03600261314984889063ffffffff16600052602052604060002090565b015416936131e1565b9091565b908160e09103126100d75760c06040519160e0830183811067ffffffffffffffff8211176131d4575b604052805183526020810151602084015260408101516040840152606081015160608401526080810151608084015260a08101516131bc81610321565b60a084015201516131cc81610321565b60c082015290565b6131dc610232565b61317f565b9495939260e0909291928051946131fb6040830151151590565b156133af576000806001600160a01b0361322260208651965b01516001600160a01b031690565b6040517f195cd92c00000000000000000000000000000000000000000000000000000000815293909912600484015260248301949094526001600160a01b039097166044820152958692606492849291165af19283156133a2575b60009361334a575b5063ffffffff7fe21eb33795bf361d3419629f8b1091aef9f4c8d525bea8dd66efbbe19d05de4d916132eb6132bd6020870151611675565b98826132cc6040890151611675565b99876132d6610314565b918c83528d60208401528d60408401526133f6565b611bc961331660c061330760a08901516001600160a01b031690565b9701516001600160a01b031690565b6040519384931696898b85929094936060926080850196855260208501526001600160a01b03809216604085015216910152565b7fe21eb33795bf361d3419629f8b1091aef9f4c8d525bea8dd66efbbe19d05de4d91935061339363ffffffff9160e03d811161339b575b61338b81836102e4565b810190613156565b939150613285565b503d613381565b6133aa612294565b61327d565b6000806001600160a01b0361322260206133c88b611675565b96613214565b6005600691939293818510156133e9575b8185040193060290565b6133f1612244565b6133df565b9361350e84600293876001600160a01b03866134258561353e989063ffffffff16600052602052604060002090565b015416917f207752419b4d795bdf5fbf6fe5dc8c9f25db48ef8c82f58a5a19724babf74a2b6134ec600184019461346c87879063ffffffff16600052602052604060002090565b604051917f7779815c000000000000000000000000000000000000000000000000000000008352602083600481845afa92831561359d575b600093613571575b50816134c96134bd8e93869561364c565b9d8e99019d8e54611619565b8d55015560408051958652602086019190915263ffffffff871694918291820190565b0390a36134f982896125db565b9063ffffffff16600052602052604060002090565b9261351e60208201518554611619565b8455604081015161353460018601918254611619565b9055518254611619565b9055805415908161355c575b50613553575050565b6101a8916135aa565b6003015465ffffffffffff161590503861354a565b6134bd9350916134c96135938e939460203d8111611e4957611e3981836102e4565b94505091906134ac565b6135a5612294565b6134a4565b60018101826135c981839063ffffffff16600052602052604060002090565b54159182159261361b575b50506135e457906101a8916126ee565b60248263ffffffff604051917fd04a5725000000000000000000000000000000000000000000000000000000008352166004820152fd5b65ffffffffffff9250600391613640919063ffffffff16600052602052604060002090565b015416151582386135d4565b6001600160a01b03610a4991602061141f946004604051809581937fa3a1e7e9000000000000000000000000000000000000000000000000000000008352165afa9182156136dc575b6000926136b8575b5080600160026136b2930154910154926120e3565b906136e9565b6136b29192506136d59060203d8111611e4957611e3981836102e4565b919061369d565b6136e4612294565b613695565b909190600160008212156137395750613703600091611675565b905b80600085121561372a575061372291610efe611b02921595611675565b911561259857565b9361372292611b0292506116d0565b613705565b600f0b7fffffffffffffffffffffffffffffffff8000000000000000000000000000000081146116875760000390565b916101a8959493916113cd6fffffffffffffffffffffffffffffffff845460381c16600f0b61373e565b95929493600094600293846137cf816137c1848a9063ffffffff16600052602052604060002090565b01546001600160a01b031690565b926137d8613c70565b508985600f0b948a86139a85898d600014613b5b57505082546040517f1dce5fcf000000000000000000000000000000000000000000000000000000008152600882901c870b600290810b600483015260209290921c90960b900b6024860152506fffffffffffffffffffffffffffffffff8716604485015260c090849060649082906000906001600160a01b03165af18015613b4e575b848860009283968491613b04575b509161389f9391613899613893889594611b15565b98611b15565b95613d75565b808251906138ac916120e3565b82528a87602084019a86868d51906138c3916120e3565b8d526138de919063ffffffff16600052602052604060002090565b600101546001600160a01b03166138f490614408565b9283898861390189611675565b9361390b90611675565b946139159661417e565b61391f908b613ead565b509860018b01918254613932908c6120e3565b906040019081519061394391611619565b90528a5460381c6fffffffffffffffffffffffffffffffff16906000149563ffffffff957f841e7440ea722264a0d4fdfb3c6be43d4c3be7d0dd5334b7a2edab9e8ca845ff97613abf5750906139ab916fffffffffffffffffffffffffffffffff1690613d50565b915b6fffffffffffffffffffffffffffffffff99838b1615613a7257613a18613a1d93948d907fffffffffffffffffff00000000000000000000000000000000ffffffffffffff76ffffffffffffffffffffffffffffffff0000000000000083549260381b169116179055565b611619565b90555b613a598854965160405193849316968860201c810b908960081c900b84604091949392606082019560020b825260020b60208201520152565b0390a360381c1615613a69575050565b6101a8916141fc565b505089547fffffffffffffffffff00000000000000000000000000000000ffffffffffffff168a556000915055600085890155600060038901556000600489015560006005890155613a20565b60009094929412613ad1575b506139ad565b92613af7613ae2613afd939561373e565b6fffffffffffffffffffffffffffffffff1690565b90613d2f565b9138613acb565b613899975061389f94508693915091613b366138939360c03d8111613b47575b613b2e81836102e4565b810190613d0f565b99919490999395505050919361387e565b503d613b24565b613b56612294565b613870565b90919560c090613beb948b896001600160a01b03613b7d613ae28b549461373e565b94604051998a96879586937fa34123a70000000000000000000000000000000000000000000000000000000085528160201c810b9160081c900b600485019160409194936fffffffffffffffffffffffffffffffff91606085019660020b855260020b602085015216910152565b0393165af18015613c63575b86938791613c21575b509161389f9391613899610a49613c1b610a498a9796611b15565b99611b15565b613c1b975061389f94508693915091613899610a49613c50610a499560c03d8111613b4757613b2e81836102e4565b9b9196909b959750505050509193613c00565b613c6b612294565b613bf7565b604051906080820182811067ffffffffffffffff821117613ca7575b60405260006060838281528260208201528260408201520152565b613caf610232565b613c8c565b91908260809103126100d7576040516080810181811067ffffffffffffffff821117613d02575b60405260608082948051845260208101516020850152604081015160408501520151910152565b613d0a610232565b613cdb565b909160c0828403126100d75761141f825193604060208501519401613cb4565b6fffffffffffffffffffffffffffffffff91821691168181106116a6570390565b906fffffffffffffffffffffffffffffffff808093169116809203811161165c570190565b939190928051908460408201928351613d8e918961403e565b9283865190613d9c91611619565b865260608301928351613daf908a614141565b613db890611b15565b9687815190613dc691611619565b90528051908160028b015560200151918260038b015551938460048b0155519889600582015554948560081c60020b9560201c60020b9663ffffffff16998a95604051948594613e47948b8b8893909796959260c0959260e086019960020b865260020b602086015260408501526060840152608083015260a08201520152565b037fb9b041b729bca1f1535dde5369cddca43b655bbc9dbd3e5878047a06e11a45c791a360408051600292830b81529290910b60208301528101919091527f8415d777c210eb5ec128f3ed52b314fc5c06eb3a464c58fa8f06a56aea5e744290606090a3565b549190613ec0600884901c60020b612885565b92613ed08160201c60020b612885565b9082916001600160a01b0392838088169516858110600014613f9357505085925b83918095828616918381168311613f86575b508284169586156100d757613f62611b0297613f5c8a876fffffffffffffffffffffffffffffffff998161141f9e169203167bffffffffffffffffffffffffffffffff0000000000000000000000008b60281b1661188c565b04611b15565b998093829311613f7b575b505003169160381c1661183e565b915091503880613f6d565b9650925084959238613f03565b8483959295161015613ef15792508092613ef1565b6001600160a01b039283831684831611614036575b7bffffffffffffffffffffffffffffffff0000000000000000000000009060601b169280808385031692169283156100d7571690613ffc82828661188c565b938215614029575b09614015575b808206151591040190565b906000198110156100d7576001019061400a565b614031612853565b614004565b909190613fbd565b61141f9261407c6140829261196760028201546136b26003840154916fffffffffffffffffffffffffffffffff6004860154955460381c16986120e3565b906120e3565b9061408d81836125a2565b91600083129182918261410a575b50506140a8575b50611675565b9060001991817f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0383138115166140fd575b81600160ff1b038312166140f0575b01386140a2565b6140f8611602565b6140e9565b614105611602565b6140da565b700100000000000000000000000000000000919250614128906125c6565b9160009209614139575b388061409b565b506001614132565b9061141f916fffffffffffffffffffffffffffffffff600582015491828410614171575b5460381c1691036116d0565b614179611602565b614165565b7f90ebe5c3d0a3bbd59fb51797eff156440d75c86a623dddef804547362e66595a9460e0949763ffffffff9493976141e76001600160a01b0394546040519b8160081c60020b8d528c602083811c60020b910152600f0b60408d015260ff60608d0191166104c5565b60808a015260a08901521660c08701521693a3565b9054906fffffffffffffffffffffffffffffffff8260381c16806143bf575065ffffffffffff6005927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000008160201c62ffffff169160101b16840b011691821561438d57909181906000905b828210614314575b5080820361430e57506004905b820361428757505050565b8082036142b0575061429c906101a8926133ce565b65ffffffffffff82549160031b1b19169055565b826142eb61429c6142e36142dc6142ca866101a8996133ce565b905465ffffffffffff9160031b1c1690565b95846133ce565b9490936133ce565b919082549060031b9165ffffffffffff9283811b93849216901b16911916179055565b9061427c565b93929190846143346143296142ca84886133ce565b65ffffffffffff1690565b14614385575b61434a6143296142ca83876133ce565b1561436157614358906122c8565b90919293614267565b9293509091600090801561437f576143799150612217565b3861426f565b50614379565b91508161433a565b6040517fc712b5e900000000000000000000000000000000000000000000000000000000815260006004820152602490fd5b82606491604051917f1346bf480000000000000000000000000000000000000000000000000000000083528060081c60020b600484015260201c60020b60248301526044820152fd5b60e06001600160a01b03916004604051809481937f3850c7bd000000000000000000000000000000000000000000000000000000008352165afa908115614476575b600091614455575090565b61446d915060e03d811161300557612ff381836102e4565b50505050505090565b61447e612294565b61444a565b9060038110156104af5760ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008354169116179055565b548060081c60020b15908115916144cf575090565b905060201c60020b151590565b65ffffffffffff821690811561438d576005809260005b82811061456e575b50508214614535576101a89291614511916133ce565b90919082549060031b9165ffffffffffff9283811b93849216901b16911916179055565b6040517f5503aeb500000000000000000000000000000000000000000000000000000000815265ffffffffffff84166004820152602490fd5b8161457f6143296142ca84886133ce565b146126b4576145946143296142ca83876133ce565b156145a7576145a2906122c8565b6144f3565b9350389050806144fb565b91613152949391604051926145c684610249565b8352600060208401526000604084015260006060840152600060808401526001600160a01b03600261314984889063ffffffff16600052602052604060002090565b6000916000905b600860ff83161061462a575b50506002015461141f91611619565b909263ffffffff61463b858561225b565b919054600392831b1c169182156146f05791611967611a4f926146ea946146758460018a019063ffffffff16600052602052604060002090565b9187856146e06146c96146888385614a17565b96905061196761469788614898565b976001600160a01b0380976146bf60029889929063ffffffff16600052602052604060002090565b015416908a614bde565b978b9063ffffffff16600052602052604060002090565b01541692016146f8565b9061460f565b94505061461b565b600093928492835b60058510614710575b5050505050565b909192939565ffffffffffff61472688876133ce565b90549060031b1c1680156148655761481a9161196761475d6148149360018a019065ffffffffffff16600052602052604060002090565b611967614781613a186147708a85613ead565b919061477b8c612468565b906125a2565b8254604080517f2a99a792000000000000000000000000000000000000000000000000000000008152600883901c600290810b600483015260209390931c90920b6024830152939160809182818e816001600160a01b0381604481010392165afa928315614858575b8b93614823575b505061480890611967611b02606085015186614141565b9381519101519161403e565b966122c8565b93929190614700565b614808929350908161484992903d10614851575b61484181836102e4565b81019061486c565b9190386147f1565b503d614837565b614860612294565b6147ea565b5095614709565b906080828203126100d75761141f91613cb4565b6001600160a01b0391821691168181106116a6570390565b6401000276a373fffd8963efd1fc6a506488495d951d5263988d2591600180821080156149d2575b61497b57805b6148ff575b506001600160a01b03808416908316141590816148ed575b5015611ec5575090565b90506148f883615895565b14386148e3565b928361491a61490e8584614880565b6001600160a01b031690565b11156149755761495261494c6149308584614880565b60011c737fffffffffffffffffffffffffffffffffffffff1690565b846149fb565b908261495d83615895565b111561496b575092806148c6565b93909250806148c6565b926148cb565b6040517ff7b6b2e9000000000000000000000000000000000000000000000000000000008152600481018390526401000276a3602482015273fffd8963efd1fc6a506488495d951d5263988d256044820152606490fd5b507ffffb12cdf108779ad387f409f1a66d1a2f5124ea64410ed5b3a4d30149805a4f82116148c0565b906001600160a01b03808093169116809203811161165c570190565b919091614a226123bc565b90614a4084600983019063ffffffff16600052602052604060002090565b9182549463ffffffff80921682871614600014614ab5575050506001015460ff8160e01c16600014614a90577bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1691508190565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169160201c90565b919450915060009160206003614adb84889063ffffffff16600052602052604060002090565b0154916024604051809481937fdd86c1010000000000000000000000000000000000000000000000000000000083528160301c16600483015260601c5afa928315614bd1575b92614ba6575b506003614b5482614b3e6123b7614b609589612321565b969063ffffffff16600052602052604060002090565b015460201c61ffff1690565b83821115614b9057614b8161ffff614b788685612227565b925b1683611722565b10614b8a579190565b80925090565b614b8161ffff614ba08487612227565b92614b7a565b614b60919250614b54614bc860039260203d8111611e4957611e3981836102e4565b93925050614b27565b614bd9612294565b614b21565b9061141f92614bf16119679284546125a2565b9261364c565b90614c01826122a1565b604090614c10825191826102e4565b838152601f19614c2082956122a1565b019160005b838110614c325750505050565b6020908251614c4081610249565b600081528260008183015260008583015260606000818401526080830152828601015201614c25565b6002918282015492614c7a8361280a565b90614c8482614bf7565b9360005b838110614c955750505050565b614c9f818361225b565b905460039190911b1c63ffffffff16614cb882886122ff565b5163ffffffff9091169052600180830190614cd3838561225b565b905463ffffffff60039290921b1c16600090815260208390526040902054614cfb848a6122ff565b5160200152614d0a838561225b565b905463ffffffff60039290921b1c1660009081526020839052604090200154614d3383896122ff565b516040015283614d43838561225b565b905463ffffffff60039290921b1c1660009081526020839052604090200154614d6c83896122ff565b5160600152614d7b828461225b565b905460039190911b1c63ffffffff16614da3919063ffffffff16600052602052604060002090565b600301614daf90614e68565b614db982886122ff565b5160800152614dc7906122c8565b614c88565b90614dd6826122a1565b604090614de5825191826102e4565b838152601f19614df582956122a1565b019160005b838110614e075750505050565b6020908251614e15816102c7565b60008152826000818301526000858301526000606083015260006080830152600060a0830152600060c0830152600060e08301526000610100830152828601015201614dfa565b60038210156104af5752565b90614e7282615115565b614e7b81614dcc565b9260005b828110614e8b57505050565b600180830190614e9b83856133ce565b905465ffffffffffff60039290921b1c1660009081526020839052604090205460ff16614ec884896122ff565b516101000190614ed791614e5c565b614ee183856133ce565b905465ffffffffffff60039290921b1c1660009081526020839052604090205460081c60020b614f1184896122ff565b5190614f1f919060020b9052565b614f2983856133ce565b905465ffffffffffff60039290921b1c1660009081526020839052604090205460201c60020b614f5984896122ff565b5160200190614f6a919060020b9052565b614f7483856133ce565b905465ffffffffffff60039290921b1c1660009081526020839052604090205460381c6fffffffffffffffffffffffffffffffff16614fb384896122ff565b5160400190614fd391906fffffffffffffffffffffffffffffffff169052565b614fdd83856133ce565b905465ffffffffffff60039290921b1c166000908152602083905260409020015461500883886122ff565b516060015261501782846133ce565b905465ffffffffffff60039290921b1c1660009081526020829052604090206002015461504483886122ff565b516080015261505382846133ce565b9054600391821b1c65ffffffffffff166000908152602083905260409020015461507d83886122ff565b5160a0015261508c82846133ce565b905465ffffffffffff60039290921b1c166000908152602082905260409020600401546150b983886122ff565b5160c001526150c882846133ce565b905460039190911b1c65ffffffffffff166150f4919065ffffffffffff16600052602052604060002090565b6005015461510282876122ff565b5160e00152615110906122c8565b614e7f565b60005b60ff811660058110156151525765ffffffffffff61513683856133ce565b90549060031b1c1615612845575061514d90612232565b615118565b505050600590565b929192600860ff61516b8484615263565b161461525b57906151908160018094019063ffffffff16600052602052604060002090565b82810154916151c86001600160a01b03856151c06003948587019a9063ffffffff16600052602052604060002090565b015416614408565b6000946151d488615115565b926004879501985b8486106151f45750505050505061141f929350611619565b90919293949661524b615251916119676152368d65ffffffffffff6152198e896133ce565b9054908a1b1c1665ffffffffffff16600052602052604060002090565b876152418a83613ead565b50910154906120e3565b976122c8565b94939291906151dc565b506000925050565b9060005b600860ff82161061527a57505050600890565b615284818461225b565b905463ffffffff908185169260031b1c1614612845576152a390612232565b615267565b60008113156152b45790565b50600090565b9080821315611ec5575090565b929091926152e98360018093019063ffffffff16600052602052604060002090565b6152f38486614a17565b91905061ffff61530283614898565b9661532060039788929063ffffffff16600052602052604060002090565b015460101c1692815495808301976000918291600486016001600160a01b03808316915b6005861061539a575b50505050505061141f9596975061538f8561538a6153858796613a1861538a996153959861537d61538599611b15565b9301546125a2565b6152a8565b61256d565b97611675565b6125a2565b90919293949565ffffffffffff6153b28f89906133ce565b905490881b1c1680156154975761547191610b79876153e661481494879065ffffffffffff16600052602052604060002090565b8054908d61545160026154328d8d6154116154058960081c860b612885565b948960201c900b612885565b98818a1683116154905780925b85831610615487578190925b1691166117f4565b956fffffffffffffffffffffffffffffffff60009660381c1691613fa8565b91015491821261547b57908161546c826125c19410156157b0565b612227565b9493929190615344565b610b796125c192612512565b5080849261542a565b899261541e565b509561534d565b929091926154c08360018093019063ffffffff16600052602052604060002090565b6154ca8486614a17565b91905061ffff6154d983614898565b966154f760039788929063ffffffff16600052602052604060002090565b01541692815495808301976000918291600486016001600160a01b03808316915b600586106155505750505050505061141f9596975061538f8561538a6153858796613a1861538a996153959861537d61538599611b15565b90919293949565ffffffffffff6155688f89906133ce565b905490881b1c1680156154975761559c91610b79876153e661481494879065ffffffffffff16600052602052604060002090565b9493929190615518565b929091926155c88360018093019063ffffffff16600052602052604060002090565b6155d28486614a17565b91905061ffff6155e183614898565b966155ff60039788929063ffffffff16600052602052604060002090565b01541692815495808301976000918291600486016001600160a01b03808316915b600586106156585750505050505061141f9596975061538f8561538a6153858796613a1861538a996153959861537d61538599611b15565b90919293949565ffffffffffff6156708f89906133ce565b905490881b1c168015615497576156a491610b79876153e661481494879065ffffffffffff16600052602052604060002090565b9493929190615620565b9293916156ef6156d28260018097019063ffffffff16600052602052604060002090565b926156dd8388614a17565b9390506156e984614898565b976157cd565b918054946003808301976000918291600486016001600160a01b03808316915b60058610615753575b50505050505061141f9596975061538f61ffff61574a6153858796613a1861538a996153959861537d61538599611b15565b9616809661256d565b90919293949565ffffffffffff61576b8f89906133ce565b905490881b1c1680156157a95761579f91610b79876153e661481494879065ffffffffffff16600052602052604060002090565b949392919061570f565b5095615718565b156157b757565b634e487b7160e01b600052600160045260246000fd5b60039161ffff936000146157fa576157f4919063ffffffff16600052602052604060002090565b01541690565b615813919063ffffffff16600052602052604060002090565b015460101c1690565b600860ff61582a8484615263565b161461584d5790600161141f92019063ffffffff16600052602052604060002090565b60248263ffffffff604051917f8bff134b000000000000000000000000000000000000000000000000000000008352166004820152fd5b90600161141f926121f983826125db565b6001600160a01b0361141f9116806117f456fea2646970667358221220e2f332f9dcc4d7a1b2c879eba7eb141499a511caf098179ba6b07afd7a5f922464736f6c634300080e0033
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.