Contract Overview
My Name Tag:
Not Available, login to update
Latest 1 internal transaction
Parent Txn Hash | Block | From | To | Value | |||
---|---|---|---|---|---|---|---|
0x143f7f60117a890864ed938c92a47c1bb0b1ef7c601cbfe376b3fa629f31f6ba | 5165811 | 186 days 16 hrs ago | 0xe4dfd4ad723b5db11aa41d53603db03b117ec690 | Contract Creation | 0 ETH |
[ Download CSV Export ]
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xFeAf6a82616B5699E65B0557d9104DF57aDDa481 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
AlgebraPool
Compiler Version
v0.7.6+commit.7338295f
Optimization Enabled:
Yes with 0 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import './interfaces/IAlgebraPool.sol'; import './interfaces/IDataStorageOperator.sol'; import './interfaces/IAlgebraVirtualPool.sol'; import './base/PoolState.sol'; import './base/PoolImmutables.sol'; import './libraries/TokenDeltaMath.sol'; import './libraries/PriceMovementMath.sol'; import './libraries/TickManager.sol'; import './libraries/TickTable.sol'; import './libraries/LowGasSafeMath.sol'; import './libraries/SafeCast.sol'; import './libraries/FullMath.sol'; import './libraries/Constants.sol'; import './libraries/TransferHelper.sol'; import './libraries/TickMath.sol'; import './libraries/LiquidityMath.sol'; import './interfaces/IAlgebraPoolDeployer.sol'; import './interfaces/IAlgebraFactory.sol'; import './interfaces/IERC20Minimal.sol'; import './interfaces/callback/IAlgebraMintCallback.sol'; import './interfaces/callback/IAlgebraSwapCallback.sol'; import './interfaces/callback/IAlgebraFlashCallback.sol'; /// @title Algebra concentrated liquidity pool /// @notice This contract is responsible for liquidity positions, swaps and flashloans /// @dev Version: Algebra V1.9 contract AlgebraPool is PoolState, PoolImmutables, IAlgebraPool { using LowGasSafeMath for uint256; using LowGasSafeMath for int256; using LowGasSafeMath for uint128; using SafeCast for uint256; using SafeCast for int256; using TickTable for mapping(int16 => uint256); using TickManager for mapping(int24 => TickManager.Tick); struct Position { uint128 liquidity; // The amount of liquidity concentrated in the range uint32 lastLiquidityAddTimestamp; // Timestamp of last adding of liquidity uint256 innerFeeGrowth0Token; // The last updated fee growth per unit of liquidity uint256 innerFeeGrowth1Token; uint128 fees0; // The amount of token0 owed to a LP uint128 fees1; // The amount of token1 owed to a LP } /// @inheritdoc IAlgebraPoolState mapping(bytes32 => Position) public override positions; /// @dev Restricts everyone calling a function except factory owner modifier onlyFactoryOwner() { require(msg.sender == IAlgebraFactory(factory).owner()); _; } modifier onlyValidTicks(int24 bottomTick, int24 topTick) { require(topTick < TickMath.MAX_TICK + 1, 'TUM'); require(topTick > bottomTick, 'TLU'); require(bottomTick > TickMath.MIN_TICK - 1, 'TLM'); _; } constructor() PoolImmutables(msg.sender) { globalState.fee = Constants.BASE_FEE; tickSpacing = 60; } function balanceToken0() private view returns (uint256) { return IERC20Minimal(token0).balanceOf(address(this)); } function balanceToken1() private view returns (uint256) { return IERC20Minimal(token1).balanceOf(address(this)); } /// @inheritdoc IAlgebraPoolState function timepoints(uint256 index) external view override returns ( bool initialized, uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulative, uint88 volatilityCumulative, int24 averageTick, uint144 volumePerLiquidityCumulative ) { return IDataStorageOperator(dataStorageOperator).timepoints(index); } struct Cumulatives { int56 tickCumulative; uint160 outerSecondPerLiquidity; uint32 outerSecondsSpent; } /// @inheritdoc IAlgebraPoolDerivedState function getInnerCumulatives(int24 bottomTick, int24 topTick) external view override onlyValidTicks(bottomTick, topTick) returns ( int56 innerTickCumulative, uint160 innerSecondsSpentPerLiquidity, uint32 innerSecondsSpent ) { Cumulatives memory lower; { TickManager.Tick storage _lower = ticks[bottomTick]; (lower.tickCumulative, lower.outerSecondPerLiquidity, lower.outerSecondsSpent) = ( _lower.outerTickCumulative, _lower.outerSecondsPerLiquidity, _lower.outerSecondsSpent ); require(_lower.initialized); } Cumulatives memory upper; { TickManager.Tick storage _upper = ticks[topTick]; (upper.tickCumulative, upper.outerSecondPerLiquidity, upper.outerSecondsSpent) = ( _upper.outerTickCumulative, _upper.outerSecondsPerLiquidity, _upper.outerSecondsSpent ); require(_upper.initialized); } (int24 currentTick, uint16 currentTimepointIndex) = (globalState.tick, globalState.timepointIndex); if (currentTick < bottomTick) { return ( lower.tickCumulative - upper.tickCumulative, lower.outerSecondPerLiquidity - upper.outerSecondPerLiquidity, lower.outerSecondsSpent - upper.outerSecondsSpent ); } if (currentTick < topTick) { uint32 globalTime = _blockTimestamp(); (int56 globalTickCumulative, uint160 globalSecondsPerLiquidityCumulative, , ) = _getSingleTimepoint( globalTime, 0, currentTick, currentTimepointIndex, liquidity ); return ( globalTickCumulative - lower.tickCumulative - upper.tickCumulative, globalSecondsPerLiquidityCumulative - lower.outerSecondPerLiquidity - upper.outerSecondPerLiquidity, globalTime - lower.outerSecondsSpent - upper.outerSecondsSpent ); } return ( upper.tickCumulative - lower.tickCumulative, upper.outerSecondPerLiquidity - lower.outerSecondPerLiquidity, upper.outerSecondsSpent - lower.outerSecondsSpent ); } /// @inheritdoc IAlgebraPoolDerivedState function getTimepoints(uint32[] calldata secondsAgos) external view override returns ( int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, uint112[] memory volatilityCumulatives, uint256[] memory volumePerAvgLiquiditys ) { return IDataStorageOperator(dataStorageOperator).getTimepoints( _blockTimestamp(), secondsAgos, globalState.tick, globalState.timepointIndex, liquidity ); } /// @inheritdoc IAlgebraPoolActions function initialize(uint160 initialPrice) external override { require(globalState.price == 0, 'AI'); // getTickAtSqrtRatio checks validity of initialPrice inside int24 tick = TickMath.getTickAtSqrtRatio(initialPrice); uint8 defaultCommunityFee = IAlgebraFactory(factory).defaultCommunityFee(); uint32 timestamp = _blockTimestamp(); IDataStorageOperator(dataStorageOperator).initialize(timestamp, tick); globalState.price = initialPrice; globalState.communityFeeToken0 = defaultCommunityFee; globalState.communityFeeToken1 = defaultCommunityFee; globalState.unlocked = true; globalState.tick = tick; emit Initialize(initialPrice, tick); } /** * @notice Increases amounts of tokens owed to owner of the position * @param _position The position object to operate with * @param liquidityDelta The amount on which to increase\decrease the liquidity * @param innerFeeGrowth0Token Total fee token0 fee growth per 1/liquidity between position's lower and upper ticks * @param innerFeeGrowth1Token Total fee token1 fee growth per 1/liquidity between position's lower and upper ticks */ function _recalculatePosition( Position storage _position, int128 liquidityDelta, uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token ) internal { (uint128 currentLiquidity, uint32 lastLiquidityAddTimestamp) = (_position.liquidity, _position.lastLiquidityAddTimestamp); if (liquidityDelta == 0) { require(currentLiquidity > 0, 'NP'); // Do not recalculate the empty ranges } else { if (liquidityDelta < 0) { uint32 _liquidityCooldown = liquidityCooldown; if (_liquidityCooldown > 0) { require((_blockTimestamp() - lastLiquidityAddTimestamp) >= _liquidityCooldown); } } // change position liquidity uint128 liquidityNext = LiquidityMath.addDelta(currentLiquidity, liquidityDelta); (_position.liquidity, _position.lastLiquidityAddTimestamp) = ( liquidityNext, liquidityNext > 0 ? (liquidityDelta > 0 ? _blockTimestamp() : lastLiquidityAddTimestamp) : 0 ); } // update the position uint256 _innerFeeGrowth0Token = _position.innerFeeGrowth0Token; uint256 _innerFeeGrowth1Token = _position.innerFeeGrowth1Token; uint128 fees0; if (innerFeeGrowth0Token != _innerFeeGrowth0Token) { _position.innerFeeGrowth0Token = innerFeeGrowth0Token; fees0 = uint128(FullMath.mulDiv(innerFeeGrowth0Token - _innerFeeGrowth0Token, currentLiquidity, Constants.Q128)); } uint128 fees1; if (innerFeeGrowth1Token != _innerFeeGrowth1Token) { _position.innerFeeGrowth1Token = innerFeeGrowth1Token; fees1 = uint128(FullMath.mulDiv(innerFeeGrowth1Token - _innerFeeGrowth1Token, currentLiquidity, Constants.Q128)); } // To avoid overflow owner has to collect fee before it if (fees0 | fees1 != 0) { _position.fees0 += fees0; _position.fees1 += fees1; } } struct UpdatePositionCache { uint160 price; // The square root of the current price in Q64.96 format int24 tick; // The current tick uint16 timepointIndex; // The index of the last written timepoint } /** * @dev Updates position's ticks and its fees * @return position The Position object to operate with * @return amount0 The amount of token0 the caller needs to send, negative if the pool needs to send it * @return amount1 The amount of token1 the caller needs to send, negative if the pool needs to send it */ function _updatePositionTicksAndFees( address owner, int24 bottomTick, int24 topTick, int128 liquidityDelta ) private returns ( Position storage position, int256 amount0, int256 amount1 ) { UpdatePositionCache memory cache = UpdatePositionCache(globalState.price, globalState.tick, globalState.timepointIndex); position = getOrCreatePosition(owner, bottomTick, topTick); (uint256 _totalFeeGrowth0Token, uint256 _totalFeeGrowth1Token) = (totalFeeGrowth0Token, totalFeeGrowth1Token); bool toggledBottom; bool toggledTop; if (liquidityDelta != 0) { uint32 time = _blockTimestamp(); (int56 tickCumulative, uint160 secondsPerLiquidityCumulative, , ) = _getSingleTimepoint(time, 0, cache.tick, cache.timepointIndex, liquidity); if ( ticks.update( bottomTick, cache.tick, liquidityDelta, _totalFeeGrowth0Token, _totalFeeGrowth1Token, secondsPerLiquidityCumulative, tickCumulative, time, false // isTopTick ) ) { toggledBottom = true; tickTable.toggleTick(bottomTick); } if ( ticks.update( topTick, cache.tick, liquidityDelta, _totalFeeGrowth0Token, _totalFeeGrowth1Token, secondsPerLiquidityCumulative, tickCumulative, time, true // isTopTick ) ) { toggledTop = true; tickTable.toggleTick(topTick); } } (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getInnerFeeGrowth( bottomTick, topTick, cache.tick, _totalFeeGrowth0Token, _totalFeeGrowth1Token ); _recalculatePosition(position, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); if (liquidityDelta != 0) { // if liquidityDelta is negative and the tick was toggled, it means that it should not be initialized anymore, so we delete it if (liquidityDelta < 0) { if (toggledBottom) delete ticks[bottomTick]; if (toggledTop) delete ticks[topTick]; } int128 globalLiquidityDelta; (amount0, amount1, globalLiquidityDelta) = _getAmountsForLiquidity(bottomTick, topTick, liquidityDelta, cache.tick, cache.price); if (globalLiquidityDelta != 0) { uint128 liquidityBefore = liquidity; uint16 newTimepointIndex = _writeTimepoint(cache.timepointIndex, _blockTimestamp(), cache.tick, liquidityBefore, volumePerLiquidityInBlock); if (cache.timepointIndex != newTimepointIndex) { globalState.fee = _getNewFee(_blockTimestamp(), cache.tick, newTimepointIndex, liquidityBefore); globalState.timepointIndex = newTimepointIndex; volumePerLiquidityInBlock = 0; } liquidity = LiquidityMath.addDelta(liquidityBefore, liquidityDelta); } } } function _getAmountsForLiquidity( int24 bottomTick, int24 topTick, int128 liquidityDelta, int24 currentTick, uint160 currentPrice ) private pure returns ( int256 amount0, int256 amount1, int128 globalLiquidityDelta ) { // If current tick is less than the provided bottom one then only the token0 has to be provided if (currentTick < bottomTick) { amount0 = TokenDeltaMath.getToken0Delta(TickMath.getSqrtRatioAtTick(bottomTick), TickMath.getSqrtRatioAtTick(topTick), liquidityDelta); } else if (currentTick < topTick) { amount0 = TokenDeltaMath.getToken0Delta(currentPrice, TickMath.getSqrtRatioAtTick(topTick), liquidityDelta); amount1 = TokenDeltaMath.getToken1Delta(TickMath.getSqrtRatioAtTick(bottomTick), currentPrice, liquidityDelta); globalLiquidityDelta = liquidityDelta; } // If current tick is greater than the provided top one then only the token1 has to be provided else { amount1 = TokenDeltaMath.getToken1Delta(TickMath.getSqrtRatioAtTick(bottomTick), TickMath.getSqrtRatioAtTick(topTick), liquidityDelta); } } /** * @notice This function fetches certain position object * @param owner The address owing the position * @param bottomTick The position's bottom tick * @param topTick The position's top tick * @return position The Position object */ function getOrCreatePosition( address owner, int24 bottomTick, int24 topTick ) private view returns (Position storage) { bytes32 key; assembly { key := or(shl(24, or(shl(24, owner), and(bottomTick, 0xFFFFFF))), and(topTick, 0xFFFFFF)) } return positions[key]; } /// @inheritdoc IAlgebraPoolActions function mint( address sender, address recipient, int24 bottomTick, int24 topTick, uint128 liquidityDesired, bytes calldata data ) external override lock onlyValidTicks(bottomTick, topTick) returns ( uint256 amount0, uint256 amount1, uint128 liquidityActual ) { require(liquidityDesired > 0, 'IL'); { int24 _tickSpacing = tickSpacing; require(bottomTick % _tickSpacing | topTick % _tickSpacing == 0, 'tick is not spaced'); // ensure that the tick is spaced } { (int256 amount0Int, int256 amount1Int, ) = _getAmountsForLiquidity( bottomTick, topTick, int256(liquidityDesired).toInt128(), globalState.tick, globalState.price ); amount0 = uint256(amount0Int); amount1 = uint256(amount1Int); } uint256 receivedAmount0; uint256 receivedAmount1; { if (amount0 > 0) receivedAmount0 = balanceToken0(); if (amount1 > 0) receivedAmount1 = balanceToken1(); IAlgebraMintCallback(msg.sender).algebraMintCallback(amount0, amount1, data); if (amount0 > 0) require((receivedAmount0 = balanceToken0() - receivedAmount0) > 0, 'IIAM'); if (amount1 > 0) require((receivedAmount1 = balanceToken1() - receivedAmount1) > 0, 'IIAM'); } liquidityActual = liquidityDesired; if (receivedAmount0 < amount0) { liquidityActual = uint128(FullMath.mulDiv(uint256(liquidityDesired), receivedAmount0, amount0)); } if (receivedAmount1 < amount1) { uint128 liquidityForRA1 = uint128(FullMath.mulDiv(uint256(liquidityDesired), receivedAmount1, amount1)); if (liquidityForRA1 < liquidityActual) { liquidityActual = liquidityForRA1; } } require(liquidityActual > 0, 'IIL2'); { (, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees(recipient, bottomTick, topTick, int256(liquidityActual).toInt128()); require((amount0 = uint256(amount0Int)) <= receivedAmount0, 'IIAM2'); require((amount1 = uint256(amount1Int)) <= receivedAmount1, 'IIAM2'); } if (receivedAmount0 > amount0) { TransferHelper.safeTransfer(token0, sender, receivedAmount0 - amount0); } if (receivedAmount1 > amount1) { TransferHelper.safeTransfer(token1, sender, receivedAmount1 - amount1); } emit Mint(msg.sender, recipient, bottomTick, topTick, liquidityActual, amount0, amount1); } /// @inheritdoc IAlgebraPoolActions function collect( address recipient, int24 bottomTick, int24 topTick, uint128 amount0Requested, uint128 amount1Requested ) external override lock returns (uint128 amount0, uint128 amount1) { Position storage position = getOrCreatePosition(msg.sender, bottomTick, topTick); (uint128 positionFees0, uint128 positionFees1) = (position.fees0, position.fees1); amount0 = amount0Requested > positionFees0 ? positionFees0 : amount0Requested; amount1 = amount1Requested > positionFees1 ? positionFees1 : amount1Requested; if (amount0 | amount1 != 0) { position.fees0 = positionFees0 - amount0; position.fees1 = positionFees1 - amount1; if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0); if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1); } emit Collect(msg.sender, recipient, bottomTick, topTick, amount0, amount1); } /// @inheritdoc IAlgebraPoolActions function burn( int24 bottomTick, int24 topTick, uint128 amount ) external override lock onlyValidTicks(bottomTick, topTick) returns (uint256 amount0, uint256 amount1) { (Position storage position, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees( msg.sender, bottomTick, topTick, -int256(amount).toInt128() ); amount0 = uint256(-amount0Int); amount1 = uint256(-amount1Int); if (amount0 | amount1 != 0) { (position.fees0, position.fees1) = (position.fees0.add128(uint128(amount0)), position.fees1.add128(uint128(amount1))); } emit Burn(msg.sender, bottomTick, topTick, amount, amount0, amount1); } /// @dev Returns new fee according combination of sigmoids function _getNewFee( uint32 _time, int24 _tick, uint16 _index, uint128 _liquidity ) private returns (uint16 newFee) { newFee = IDataStorageOperator(dataStorageOperator).getFee(_time, _tick, _index, _liquidity); emit Fee(newFee); } function _payCommunityFee(address token, uint256 amount) private { address vault = IAlgebraFactory(factory).vaultAddress(); TransferHelper.safeTransfer(token, vault, amount); } function _writeTimepoint( uint16 timepointIndex, uint32 blockTimestamp, int24 tick, uint128 liquidity, uint128 volumePerLiquidityInBlock ) private returns (uint16 newTimepointIndex) { return IDataStorageOperator(dataStorageOperator).write(timepointIndex, blockTimestamp, tick, liquidity, volumePerLiquidityInBlock); } function _getSingleTimepoint( uint32 blockTimestamp, uint32 secondsAgo, int24 startTick, uint16 timepointIndex, uint128 liquidityStart ) private view returns ( int56 tickCumulative, uint160 secondsPerLiquidityCumulative, uint112 volatilityCumulative, uint256 volumePerAvgLiquidity ) { return IDataStorageOperator(dataStorageOperator).getSingleTimepoint(blockTimestamp, secondsAgo, startTick, timepointIndex, liquidityStart); } function _swapCallback( int256 amount0, int256 amount1, bytes calldata data ) private { IAlgebraSwapCallback(msg.sender).algebraSwapCallback(amount0, amount1, data); } /// @inheritdoc IAlgebraPoolActions function swap( address recipient, bool zeroToOne, int256 amountRequired, uint160 limitSqrtPrice, bytes calldata data ) external override returns (int256 amount0, int256 amount1) { uint160 currentPrice; int24 currentTick; uint128 currentLiquidity; uint256 communityFee; // function _calculateSwapAndLock locks globalState.unlocked and does not release (amount0, amount1, currentPrice, currentTick, currentLiquidity, communityFee) = _calculateSwapAndLock(zeroToOne, amountRequired, limitSqrtPrice); if (zeroToOne) { if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); // transfer to recipient uint256 balance0Before = balanceToken0(); _swapCallback(amount0, amount1, data); // callback to get tokens from the caller require(balance0Before.add(uint256(amount0)) <= balanceToken0(), 'IIA'); } else { if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); // transfer to recipient uint256 balance1Before = balanceToken1(); _swapCallback(amount0, amount1, data); // callback to get tokens from the caller require(balance1Before.add(uint256(amount1)) <= balanceToken1(), 'IIA'); } if (communityFee > 0) { _payCommunityFee(zeroToOne ? token0 : token1, communityFee); } emit Swap(msg.sender, recipient, amount0, amount1, currentPrice, currentLiquidity, currentTick); globalState.unlocked = true; // release after lock in _calculateSwapAndLock } /// @inheritdoc IAlgebraPoolActions function swapSupportingFeeOnInputTokens( address sender, address recipient, bool zeroToOne, int256 amountRequired, uint160 limitSqrtPrice, bytes calldata data ) external override returns (int256 amount0, int256 amount1) { // Since the pool can get less tokens then sent, firstly we are getting tokens from the // original caller of the transaction. And change the _amountRequired_ require(globalState.unlocked, 'LOK'); globalState.unlocked = false; if (zeroToOne) { uint256 balance0Before = balanceToken0(); _swapCallback(amountRequired, 0, data); require((amountRequired = int256(balanceToken0().sub(balance0Before))) > 0, 'IIA'); } else { uint256 balance1Before = balanceToken1(); _swapCallback(0, amountRequired, data); require((amountRequired = int256(balanceToken1().sub(balance1Before))) > 0, 'IIA'); } globalState.unlocked = true; uint160 currentPrice; int24 currentTick; uint128 currentLiquidity; uint256 communityFee; // function _calculateSwapAndLock locks 'globalState.unlocked' and does not release (amount0, amount1, currentPrice, currentTick, currentLiquidity, communityFee) = _calculateSwapAndLock(zeroToOne, amountRequired, limitSqrtPrice); // only transfer to the recipient if (zeroToOne) { if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); // return the leftovers if (amount0 < amountRequired) TransferHelper.safeTransfer(token0, sender, uint256(amountRequired.sub(amount0))); } else { if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); // return the leftovers if (amount1 < amountRequired) TransferHelper.safeTransfer(token1, sender, uint256(amountRequired.sub(amount1))); } if (communityFee > 0) { _payCommunityFee(zeroToOne ? token0 : token1, communityFee); } emit Swap(msg.sender, recipient, amount0, amount1, currentPrice, currentLiquidity, currentTick); globalState.unlocked = true; // release after lock in _calculateSwapAndLock } struct SwapCalculationCache { uint256 communityFee; // The community fee of the selling token, uint256 to minimize casts uint128 volumePerLiquidityInBlock; int56 tickCumulative; // The global tickCumulative at the moment uint160 secondsPerLiquidityCumulative; // The global secondPerLiquidity at the moment bool computedLatestTimepoint; // if we have already fetched _tickCumulative_ and _secondPerLiquidity_ from the DataOperator int256 amountRequiredInitial; // The initial value of the exact input\output amount int256 amountCalculated; // The additive amount of total output\input calculated trough the swap uint256 totalFeeGrowth; // The initial totalFeeGrowth + the fee growth during a swap uint256 totalFeeGrowthB; IAlgebraVirtualPool.Status incentiveStatus; // If there is an active incentive at the moment bool exactInput; // Whether the exact input or output is specified uint16 fee; // The current dynamic fee int24 startTick; // The tick at the start of a swap uint16 timepointIndex; // The index of last written timepoint } struct PriceMovementCache { uint160 stepSqrtPrice; // The Q64.96 sqrt of the price at the start of the step int24 nextTick; // The tick till the current step goes bool initialized; // True if the _nextTick is initialized uint160 nextTickPrice; // The Q64.96 sqrt of the price calculated from the _nextTick uint256 input; // The additive amount of tokens that have been provided uint256 output; // The additive amount of token that have been withdrawn uint256 feeAmount; // The total amount of fee earned within a current step } /// @notice For gas optimization, locks 'globalState.unlocked' and does not release. function _calculateSwapAndLock( bool zeroToOne, int256 amountRequired, uint160 limitSqrtPrice ) private returns ( int256 amount0, int256 amount1, uint160 currentPrice, int24 currentTick, uint128 currentLiquidity, uint256 communityFeeAmount ) { uint32 blockTimestamp; SwapCalculationCache memory cache; { // load from one storage slot currentPrice = globalState.price; currentTick = globalState.tick; cache.fee = globalState.fee; cache.timepointIndex = globalState.timepointIndex; uint256 _communityFeeToken0 = globalState.communityFeeToken0; uint256 _communityFeeToken1 = globalState.communityFeeToken1; bool unlocked = globalState.unlocked; globalState.unlocked = false; // lock will not be released in this function require(unlocked, 'LOK'); require(amountRequired != 0, 'AS'); (cache.amountRequiredInitial, cache.exactInput) = (amountRequired, amountRequired > 0); (currentLiquidity, cache.volumePerLiquidityInBlock) = (liquidity, volumePerLiquidityInBlock); if (zeroToOne) { require(limitSqrtPrice < currentPrice && limitSqrtPrice > TickMath.MIN_SQRT_RATIO, 'SPL'); cache.totalFeeGrowth = totalFeeGrowth0Token; cache.communityFee = _communityFeeToken0; } else { require(limitSqrtPrice > currentPrice && limitSqrtPrice < TickMath.MAX_SQRT_RATIO, 'SPL'); cache.totalFeeGrowth = totalFeeGrowth1Token; cache.communityFee = _communityFeeToken1; } cache.startTick = currentTick; blockTimestamp = _blockTimestamp(); if (activeIncentive != address(0)) { IAlgebraVirtualPool.Status _status = IAlgebraVirtualPool(activeIncentive).increaseCumulative(blockTimestamp); if (_status == IAlgebraVirtualPool.Status.NOT_EXIST) { activeIncentive = address(0); } else if (_status == IAlgebraVirtualPool.Status.ACTIVE) { cache.incentiveStatus = IAlgebraVirtualPool.Status.ACTIVE; } else if (_status == IAlgebraVirtualPool.Status.NOT_STARTED) { cache.incentiveStatus = IAlgebraVirtualPool.Status.NOT_STARTED; } } uint16 newTimepointIndex = _writeTimepoint( cache.timepointIndex, blockTimestamp, cache.startTick, currentLiquidity, cache.volumePerLiquidityInBlock ); // new timepoint appears only for first swap in block if (newTimepointIndex != cache.timepointIndex) { cache.timepointIndex = newTimepointIndex; cache.volumePerLiquidityInBlock = 0; cache.fee = _getNewFee(blockTimestamp, currentTick, newTimepointIndex, currentLiquidity); } } PriceMovementCache memory step; // swap until there is remaining input or output tokens or we reach the price limit while (true) { step.stepSqrtPrice = currentPrice; (step.nextTick, step.initialized) = tickTable.nextTickInTheSameRow(currentTick, zeroToOne); step.nextTickPrice = TickMath.getSqrtRatioAtTick(step.nextTick); // calculate the amounts needed to move the price to the next target if it is possible or as much as possible (currentPrice, step.input, step.output, step.feeAmount) = PriceMovementMath.movePriceTowardsTarget( zeroToOne, currentPrice, (zeroToOne == (step.nextTickPrice < limitSqrtPrice)) // move the price to the target or to the limit ? limitSqrtPrice : step.nextTickPrice, currentLiquidity, amountRequired, cache.fee ); if (cache.exactInput) { amountRequired -= (step.input + step.feeAmount).toInt256(); // decrease remaining input amount cache.amountCalculated = cache.amountCalculated.sub(step.output.toInt256()); // decrease calculated output amount } else { amountRequired += step.output.toInt256(); // increase remaining output amount (since its negative) cache.amountCalculated = cache.amountCalculated.add((step.input + step.feeAmount).toInt256()); // increase calculated input amount } if (cache.communityFee > 0) { uint256 delta = (step.feeAmount.mul(cache.communityFee)) / Constants.COMMUNITY_FEE_DENOMINATOR; step.feeAmount -= delta; communityFeeAmount += delta; } if (currentLiquidity > 0) cache.totalFeeGrowth += FullMath.mulDiv(step.feeAmount, Constants.Q128, currentLiquidity); if (currentPrice == step.nextTickPrice) { // if the reached tick is initialized then we need to cross it if (step.initialized) { // once at a swap we have to get the last timepoint of the observation if (!cache.computedLatestTimepoint) { (cache.tickCumulative, cache.secondsPerLiquidityCumulative, , ) = _getSingleTimepoint( blockTimestamp, 0, cache.startTick, cache.timepointIndex, currentLiquidity // currentLiquidity can be changed only after computedLatestTimepoint ); cache.computedLatestTimepoint = true; cache.totalFeeGrowthB = zeroToOne ? totalFeeGrowth1Token : totalFeeGrowth0Token; } // every tick cross is needed to be duplicated in a virtual pool if (cache.incentiveStatus != IAlgebraVirtualPool.Status.NOT_EXIST) { IAlgebraVirtualPool(activeIncentive).cross(step.nextTick, zeroToOne); } int128 liquidityDelta; if (zeroToOne) { liquidityDelta = -ticks.cross( step.nextTick, cache.totalFeeGrowth, // A == 0 cache.totalFeeGrowthB, // B == 1 cache.secondsPerLiquidityCumulative, cache.tickCumulative, blockTimestamp ); } else { liquidityDelta = ticks.cross( step.nextTick, cache.totalFeeGrowthB, // B == 0 cache.totalFeeGrowth, // A == 1 cache.secondsPerLiquidityCumulative, cache.tickCumulative, blockTimestamp ); } currentLiquidity = LiquidityMath.addDelta(currentLiquidity, liquidityDelta); } currentTick = zeroToOne ? step.nextTick - 1 : step.nextTick; } else if (currentPrice != step.stepSqrtPrice) { // if the price has changed but hasn't reached the target currentTick = TickMath.getTickAtSqrtRatio(currentPrice); break; // since the price hasn't reached the target, amountRequired should be 0 } // check stop condition if (amountRequired == 0 || currentPrice == limitSqrtPrice) { break; } } (amount0, amount1) = zeroToOne == cache.exactInput // the amount to provide could be less then initially specified (e.g. reached limit) ? (cache.amountRequiredInitial - amountRequired, cache.amountCalculated) // the amount to get could be less then initially specified (e.g. reached limit) : (cache.amountCalculated, cache.amountRequiredInitial - amountRequired); (globalState.price, globalState.tick, globalState.fee, globalState.timepointIndex) = (currentPrice, currentTick, cache.fee, cache.timepointIndex); (liquidity, volumePerLiquidityInBlock) = ( currentLiquidity, cache.volumePerLiquidityInBlock + IDataStorageOperator(dataStorageOperator).calculateVolumePerLiquidity(currentLiquidity, amount0, amount1) ); if (zeroToOne) { totalFeeGrowth0Token = cache.totalFeeGrowth; } else { totalFeeGrowth1Token = cache.totalFeeGrowth; } } /// @inheritdoc IAlgebraPoolActions function flash( address recipient, uint256 amount0, uint256 amount1, bytes calldata data ) external override lock { uint128 _liquidity = liquidity; require(_liquidity > 0, 'L'); uint16 _fee = globalState.fee; uint256 fee0; uint256 balance0Before = balanceToken0(); if (amount0 > 0) { fee0 = FullMath.mulDivRoundingUp(amount0, _fee, 1e6); TransferHelper.safeTransfer(token0, recipient, amount0); } uint256 fee1; uint256 balance1Before = balanceToken1(); if (amount1 > 0) { fee1 = FullMath.mulDivRoundingUp(amount1, _fee, 1e6); TransferHelper.safeTransfer(token1, recipient, amount1); } IAlgebraFlashCallback(msg.sender).algebraFlashCallback(fee0, fee1, data); address vault = IAlgebraFactory(factory).vaultAddress(); uint256 paid0 = balanceToken0(); require(balance0Before.add(fee0) <= paid0, 'F0'); paid0 -= balance0Before; if (paid0 > 0) { uint8 _communityFeeToken0 = globalState.communityFeeToken0; uint256 fees0; if (_communityFeeToken0 > 0) { fees0 = (paid0 * _communityFeeToken0) / Constants.COMMUNITY_FEE_DENOMINATOR; TransferHelper.safeTransfer(token0, vault, fees0); } totalFeeGrowth0Token += FullMath.mulDiv(paid0 - fees0, Constants.Q128, _liquidity); } uint256 paid1 = balanceToken1(); require(balance1Before.add(fee1) <= paid1, 'F1'); paid1 -= balance1Before; if (paid1 > 0) { uint8 _communityFeeToken1 = globalState.communityFeeToken1; uint256 fees1; if (_communityFeeToken1 > 0) { fees1 = (paid1 * _communityFeeToken1) / Constants.COMMUNITY_FEE_DENOMINATOR; TransferHelper.safeTransfer(token1, vault, fees1); } totalFeeGrowth1Token += FullMath.mulDiv(paid1 - fees1, Constants.Q128, _liquidity); } emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1); } /// @inheritdoc IAlgebraPoolPermissionedActions function setCommunityFee(uint8 communityFee0, uint8 communityFee1) external override lock onlyFactoryOwner { require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE)); (globalState.communityFeeToken0, globalState.communityFeeToken1) = (communityFee0, communityFee1); emit CommunityFee(communityFee0, communityFee1); } /// @inheritdoc IAlgebraPoolPermissionedActions function setTickSpacing(int24 newTickSpacing) external override lock onlyFactoryOwner { require(newTickSpacing > 0 && newTickSpacing <= Constants.MAX_TICK_SPACING && tickSpacing != newTickSpacing, 'Invalid newTickSpacing'); tickSpacing = newTickSpacing; emit TickSpacing(newTickSpacing); } /// @inheritdoc IAlgebraPoolPermissionedActions function setIncentive(address virtualPoolAddress) external override { require(msg.sender == IAlgebraFactory(factory).farmingAddress()); activeIncentive = virtualPoolAddress; emit Incentive(virtualPoolAddress); } /// @inheritdoc IAlgebraPoolPermissionedActions function setLiquidityCooldown(uint32 newLiquidityCooldown) external override onlyFactoryOwner { require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown); liquidityCooldown = newLiquidityCooldown; emit LiquidityCooldown(newLiquidityCooldown); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import '../interfaces/pool/IAlgebraPoolImmutables.sol'; import '../interfaces/IAlgebraPoolDeployer.sol'; import '../libraries/Constants.sol'; abstract contract PoolImmutables is IAlgebraPoolImmutables { /// @inheritdoc IAlgebraPoolImmutables address public immutable override dataStorageOperator; /// @inheritdoc IAlgebraPoolImmutables address public immutable override factory; /// @inheritdoc IAlgebraPoolImmutables address public immutable override token0; /// @inheritdoc IAlgebraPoolImmutables address public immutable override token1; /// @inheritdoc IAlgebraPoolImmutables function maxLiquidityPerTick() external pure override returns (uint128) { return Constants.MAX_LIQUIDITY_PER_TICK; } constructor(address deployer) { (dataStorageOperator, factory, token0, token1) = IAlgebraPoolDeployer(deployer).parameters(); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import '../interfaces/pool/IAlgebraPoolState.sol'; import '../libraries/TickManager.sol'; abstract contract PoolState is IAlgebraPoolState { struct GlobalState { uint160 price; // The square root of the current price in Q64.96 format int24 tick; // The current tick uint16 fee; // The current fee in hundredths of a bip, i.e. 1e-6 uint16 timepointIndex; // The index of the last written timepoint uint8 communityFeeToken0; // The community fee represented as a percent of all collected fee in thousandths (1e-3) uint8 communityFeeToken1; bool unlocked; // True if the contract is unlocked, otherwise - false } /// @inheritdoc IAlgebraPoolState uint256 public override totalFeeGrowth0Token; /// @inheritdoc IAlgebraPoolState uint256 public override totalFeeGrowth1Token; /// @inheritdoc IAlgebraPoolState GlobalState public override globalState; /// @inheritdoc IAlgebraPoolState uint128 public override liquidity; uint128 internal volumePerLiquidityInBlock; /// @inheritdoc IAlgebraPoolState uint32 public override liquidityCooldown; /// @inheritdoc IAlgebraPoolState address public override activeIncentive; /// @inheritdoc IAlgebraPoolState int24 public override tickSpacing; /// @inheritdoc IAlgebraPoolState mapping(int24 => TickManager.Tick) public override ticks; /// @inheritdoc IAlgebraPoolState mapping(int16 => uint256) public override tickTable; /// @dev Reentrancy protection. Implemented in every function of the contract since there are checks of balances. modifier lock() { require(globalState.unlocked, 'LOK'); globalState.unlocked = false; _; globalState.unlocked = true; } /// @dev This function is created for testing by overriding it. /// @return A timestamp converted to uint32 function _blockTimestamp() internal view virtual returns (uint32) { return uint32(block.timestamp); // truncation is desired } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /** * @title Callback for IAlgebraPoolActions#flash * @notice Any contract that calls IAlgebraPoolActions#flash must implement this interface * @dev Credit to Uniswap Labs under GPL-2.0-or-later license: * https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces */ interface IAlgebraFlashCallback { /** * @notice Called to `msg.sender` after transferring to the recipient from IAlgebraPool#flash. * @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. * The caller of this method must be checked to be a AlgebraPool deployed by the canonical AlgebraFactory. * @param fee0 The fee amount in token0 due to the pool by the end of the flash * @param fee1 The fee amount in token1 due to the pool by the end of the flash * @param data Any data passed through by the caller via the IAlgebraPoolActions#flash call */ function algebraFlashCallback( uint256 fee0, uint256 fee1, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Callback for IAlgebraPoolActions#mint /// @notice Any contract that calls IAlgebraPoolActions#mint must implement this interface /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces interface IAlgebraMintCallback { /// @notice Called to `msg.sender` after minting liquidity to a position from IAlgebraPool#mint. /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity. /// The caller of this method must be checked to be a AlgebraPool deployed by the canonical AlgebraFactory. /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity /// @param data Any data passed through by the caller via the IAlgebraPoolActions#mint call function algebraMintCallback( uint256 amount0Owed, uint256 amount1Owed, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Callback for IAlgebraPoolActions#swap /// @notice Any contract that calls IAlgebraPoolActions#swap must implement this interface /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces interface IAlgebraSwapCallback { /// @notice Called to `msg.sender` after executing a swap via IAlgebraPool#swap. /// @dev In the implementation you must pay the pool tokens owed for the swap. /// The caller of this method must be checked to be a AlgebraPool deployed by the canonical AlgebraFactory. /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. /// @param data Any data passed through by the caller via the IAlgebraPoolActions#swap call function algebraSwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /** * @title The interface for the Algebra Factory * @dev Credit to Uniswap Labs under GPL-2.0-or-later license: * https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces */ interface IAlgebraFactory { /** * @notice Emitted when the owner of the factory is changed * @param newOwner The owner after the owner was changed */ event Owner(address indexed newOwner); /** * @notice Emitted when the vault address is changed * @param newVaultAddress The vault address after the address was changed */ event VaultAddress(address indexed newVaultAddress); /** * @notice Emitted when a pool is created * @param token0 The first token of the pool by address sort order * @param token1 The second token of the pool by address sort order * @param pool The address of the created pool */ event Pool(address indexed token0, address indexed token1, address pool); /** * @notice Emitted when the farming address is changed * @param newFarmingAddress The farming address after the address was changed */ event FarmingAddress(address indexed newFarmingAddress); /** * @notice Emitted when the default community fee is changed * @param newDefaultCommunityFee The new default community fee value */ event DefaultCommunityFee(uint8 newDefaultCommunityFee); event FeeConfiguration( uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint32 volumeBeta, uint16 volumeGamma, uint16 baseFee ); /** * @notice Returns the current owner of the factory * @dev Can be changed by the current owner via setOwner * @return The address of the factory owner */ function owner() external view returns (address); /** * @notice Returns the current poolDeployerAddress * @return The address of the poolDeployer */ function poolDeployer() external view returns (address); /** * @dev Is retrieved from the pools to restrict calling * certain functions not by a tokenomics contract * @return The tokenomics contract address */ function farmingAddress() external view returns (address); /** * @notice Returns the default community fee * @return Fee which will be set at the creation of the pool */ function defaultCommunityFee() external view returns (uint8); function vaultAddress() external view returns (address); /** * @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist * @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order * @param tokenA The contract address of either token0 or token1 * @param tokenB The contract address of the other token * @return pool The pool address */ function poolByPair(address tokenA, address tokenB) external view returns (address pool); /** * @notice Creates a pool for the given two tokens and fee * @param tokenA One of the two tokens in the desired pool * @param tokenB The other of the two tokens in the desired pool * @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved * from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments * are invalid. * @return pool The address of the newly created pool */ function createPool(address tokenA, address tokenB) external returns (address pool); /** * @notice Updates the owner of the factory * @dev Must be called by the current owner * @param _owner The new owner of the factory */ function setOwner(address _owner) external; /** * @dev updates tokenomics address on the factory * @param _farmingAddress The new tokenomics contract address */ function setFarmingAddress(address _farmingAddress) external; /** * @dev updates default community fee for new pools * @param newDefaultCommunityFee The new community fee, _must_ be <= MAX_COMMUNITY_FEE */ function setDefaultCommunityFee(uint8 newDefaultCommunityFee) external; /** * @dev updates vault address on the factory * @param _vaultAddress The new vault contract address */ function setVaultAddress(address _vaultAddress) external; /** * @notice Changes initial fee configuration for new pools * @dev changes coefficients for sigmoids: α / (1 + e^( (β-x) / γ)) * alpha1 + alpha2 + baseFee (max possible fee) must be <= type(uint16).max * gammas must be > 0 * @param alpha1 max value of the first sigmoid * @param alpha2 max value of the second sigmoid * @param beta1 shift along the x-axis for the first sigmoid * @param beta2 shift along the x-axis for the second sigmoid * @param gamma1 horizontal stretch factor for the first sigmoid * @param gamma2 horizontal stretch factor for the second sigmoid * @param volumeBeta shift along the x-axis for the outer volume-sigmoid * @param volumeGamma horizontal stretch factor the outer volume-sigmoid * @param baseFee minimum possible fee */ function setBaseFeeConfiguration( uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint32 volumeBeta, uint16 volumeGamma, uint16 baseFee ) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import './pool/IAlgebraPoolImmutables.sol'; import './pool/IAlgebraPoolState.sol'; import './pool/IAlgebraPoolDerivedState.sol'; import './pool/IAlgebraPoolActions.sol'; import './pool/IAlgebraPoolPermissionedActions.sol'; import './pool/IAlgebraPoolEvents.sol'; /** * @title The interface for a Algebra Pool * @dev The pool interface is broken up into many smaller pieces. * Credit to Uniswap Labs under GPL-2.0-or-later license: * https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces */ interface IAlgebraPool is IAlgebraPoolImmutables, IAlgebraPoolState, IAlgebraPoolDerivedState, IAlgebraPoolActions, IAlgebraPoolPermissionedActions, IAlgebraPoolEvents { // used only for combining interfaces }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /** * @title An interface for a contract that is capable of deploying Algebra Pools * @notice A contract that constructs a pool must implement this to pass arguments to the pool * @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash * of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain. * Credit to Uniswap Labs under GPL-2.0-or-later license: * https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces */ interface IAlgebraPoolDeployer { /** * @notice Emitted when the factory address is changed * @param factory The factory address after the address was changed */ event Factory(address indexed factory); /** * @notice Get the parameters to be used in constructing the pool, set transiently during pool creation. * @dev Called by the pool constructor to fetch the parameters of the pool * Returns dataStorage The pools associated dataStorage * Returns factory The factory address * Returns token0 The first token of the pool by address sort order * Returns token1 The second token of the pool by address sort order */ function parameters() external view returns ( address dataStorage, address factory, address token0, address token1 ); /** * @dev Deploys a pool with the given parameters by transiently setting the parameters storage slot and then * clearing it after deploying the pool. * @param dataStorage The pools associated dataStorage * @param factory The contract address of the Algebra factory * @param token0 The first token of the pool by address sort order * @param token1 The second token of the pool by address sort order * @return pool The deployed pool's address */ function deploy( address dataStorage, address factory, address token0, address token1 ) external returns (address pool); /** * @dev Sets the factory address to the poolDeployer for permissioned actions * @param factory The address of the Algebra factory */ function setFactory(address factory) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; interface IAlgebraVirtualPool { enum Status { NOT_EXIST, ACTIVE, NOT_STARTED } /** * @dev This function is called by the main pool when an initialized tick is crossed there. * If the tick is also initialized in a virtual pool it should be crossed too * @param nextTick The crossed tick * @param zeroToOne The direction */ function cross(int24 nextTick, bool zeroToOne) external; /** * @dev This function is called from the main pool before every swap To increase seconds per liquidity * cumulative considering previous timestamp and liquidity. The liquidity is stored in a virtual pool * @param currentTimestamp The timestamp of the current swap * @return Status The status of virtual pool */ function increaseCumulative(uint32 currentTimestamp) external returns (Status); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; pragma abicoder v2; import '../libraries/AdaptiveFee.sol'; interface IDataStorageOperator { event FeeConfiguration(AdaptiveFee.Configuration feeConfig); /** * @notice Returns data belonging to a certain timepoint * @param index The index of timepoint in the array * @dev There is more convenient function to fetch a timepoint: getTimepoints(). Which requires not an index but seconds * @return initialized Whether the timepoint has been initialized and the values are safe to use, * blockTimestamp The timestamp of the observation, * tickCumulative The tick multiplied by seconds elapsed for the life of the pool as of the timepoint timestamp, * secondsPerLiquidityCumulative The seconds per in range liquidity for the life of the pool as of the timepoint timestamp, * volatilityCumulative Cumulative standard deviation for the life of the pool as of the timepoint timestamp, * averageTick Time-weighted average tick, * volumePerLiquidityCumulative Cumulative swap volume per liquidity for the life of the pool as of the timepoint timestamp */ function timepoints( uint256 index ) external view returns ( bool initialized, uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulative, uint88 volatilityCumulative, int24 averageTick, uint144 volumePerLiquidityCumulative ); /// @notice Initialize the dataStorage array by writing the first slot. Called once for the lifecycle of the timepoints array /// @param time The time of the dataStorage initialization, via block.timestamp truncated to uint32 /// @param tick Initial tick function initialize(uint32 time, int24 tick) external; /// @dev Reverts if an timepoint at or before the desired timepoint timestamp does not exist. /// 0 may be passed as `secondsAgo' to return the current cumulative values. /// If called with a timestamp falling between two timepoints, returns the counterfactual accumulator values /// at exactly the timestamp between the two timepoints. /// @param time The current block timestamp /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an timepoint /// @param tick The current tick /// @param index The index of the timepoint that was most recently written to the timepoints array /// @param liquidity The current in-range pool liquidity /// @return tickCumulative The cumulative tick since the pool was first initialized, as of `secondsAgo` /// @return secondsPerLiquidityCumulative The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of `secondsAgo` /// @return volatilityCumulative The cumulative volatility value since the pool was first initialized, as of `secondsAgo` /// @return volumePerAvgLiquidity The cumulative volume per liquidity value since the pool was first initialized, as of `secondsAgo` function getSingleTimepoint( uint32 time, uint32 secondsAgo, int24 tick, uint16 index, uint128 liquidity ) external view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulative, uint112 volatilityCumulative, uint256 volumePerAvgLiquidity); /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos` /// @dev Reverts if `secondsAgos` > oldest timepoint /// @param time The current block.timestamp /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an timepoint /// @param tick The current tick /// @param index The index of the timepoint that was most recently written to the timepoints array /// @param liquidity The current in-range pool liquidity /// @return tickCumulatives The cumulative tick since the pool was first initialized, as of each `secondsAgo` /// @return secondsPerLiquidityCumulatives The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo` /// @return volatilityCumulatives The cumulative volatility values since the pool was first initialized, as of each `secondsAgo` /// @return volumePerAvgLiquiditys The cumulative volume per liquidity values since the pool was first initialized, as of each `secondsAgo` function getTimepoints( uint32 time, uint32[] memory secondsAgos, int24 tick, uint16 index, uint128 liquidity ) external view returns ( int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, uint112[] memory volatilityCumulatives, uint256[] memory volumePerAvgLiquiditys ); /// @notice Returns average volatility in the range from time-WINDOW to time /// @param time The current block.timestamp /// @param tick The current tick /// @param index The index of the timepoint that was most recently written to the timepoints array /// @param liquidity The current in-range pool liquidity /// @return TWVolatilityAverage The average volatility in the recent range /// @return TWVolumePerLiqAverage The average volume per liquidity in the recent range function getAverages( uint32 time, int24 tick, uint16 index, uint128 liquidity ) external view returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage); /// @notice Writes an dataStorage timepoint to the array /// @dev Writable at most once per block. Index represents the most recently written element. index must be tracked externally. /// @param index The index of the timepoint that was most recently written to the timepoints array /// @param blockTimestamp The timestamp of the new timepoint /// @param tick The active tick at the time of the new timepoint /// @param liquidity The total in-range liquidity at the time of the new timepoint /// @param volumePerLiquidity The gmean(volumes)/liquidity at the time of the new timepoint /// @return indexUpdated The new index of the most recently written element in the dataStorage array function write( uint16 index, uint32 blockTimestamp, int24 tick, uint128 liquidity, uint128 volumePerLiquidity ) external returns (uint16 indexUpdated); /// @notice Changes fee configuration for the pool function changeFeeConfiguration(AdaptiveFee.Configuration calldata feeConfig) external; /// @notice Calculates gmean(volume/liquidity) for block /// @param liquidity The current in-range pool liquidity /// @param amount0 Total amount of swapped token0 /// @param amount1 Total amount of swapped token1 /// @return volumePerLiquidity gmean(volume/liquidity) capped by 100000 << 64 function calculateVolumePerLiquidity(uint128 liquidity, int256 amount0, int256 amount1) external pure returns (uint128 volumePerLiquidity); /// @return windowLength Length of window used to calculate averages function window() external view returns (uint32 windowLength); /// @notice Calculates fee based on combination of sigmoids /// @param time The current block.timestamp /// @param tick The current tick /// @param index The index of the timepoint that was most recently written to the timepoints array /// @param liquidity The current in-range pool liquidity /// @return fee The fee in hundredths of a bip, i.e. 1e-6 function getFee(uint32 time, int24 tick, uint16 index, uint128 liquidity) external view returns (uint16 fee); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Minimal ERC20 interface for Algebra /// @notice Contains a subset of the full ERC20 interface that is used in Algebra /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces interface IERC20Minimal { /// @notice Returns the balance of a token /// @param account The account for which to look up the number of tokens it has, i.e. its balance /// @return The number of tokens held by the account function balanceOf(address account) external view returns (uint256); /// @notice Transfers the amount of token from the `msg.sender` to the recipient /// @param recipient The account that will receive the amount transferred /// @param amount The number of tokens to send from the sender to the recipient /// @return Returns true for a successful transfer, false for an unsuccessful transfer function transfer(address recipient, uint256 amount) external returns (bool); /// @notice Returns the current allowance given to a spender by an owner /// @param owner The account of the token owner /// @param spender The account of the token spender /// @return The current allowance granted by `owner` to `spender` function allowance(address owner, address spender) external view returns (uint256); /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` /// @param spender The account which will be allowed to spend a given amount of the owners tokens /// @param amount The amount of tokens allowed to be used by `spender` /// @return Returns true for a successful approval, false for unsuccessful function approve(address spender, uint256 amount) external returns (bool); /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` /// @param sender The account from which the transfer will be initiated /// @param recipient The recipient of the transfer /// @param amount The amount of the transfer /// @return Returns true for a successful transfer, false for unsuccessful function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. /// @param from The account from which the tokens were sent, i.e. the balance decreased /// @param to The account to which the tokens were sent, i.e. the balance increased /// @param value The amount of tokens that were transferred event Transfer(address indexed from, address indexed to, uint256 value); /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. /// @param owner The account that approved spending of its tokens /// @param spender The account for which the spending allowance was modified /// @param value The new allowance from the owner to the spender event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Permissionless pool actions /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces interface IAlgebraPoolActions { /** * @notice Sets the initial price for the pool * @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value * @param price the initial sqrt price of the pool as a Q64.96 */ function initialize(uint160 price) external; /** * @notice Adds liquidity for the given recipient/bottomTick/topTick position * @dev The caller of this method receives a callback in the form of IAlgebraMintCallback# AlgebraMintCallback * in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends * on bottomTick, topTick, the amount of liquidity, and the current price. * @param sender The address which will receive potential surplus of paid tokens * @param recipient The address for which the liquidity will be created * @param bottomTick The lower tick of the position in which to add liquidity * @param topTick The upper tick of the position in which to add liquidity * @param amount The desired 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 * @return liquidityActual The actual minted amount of liquidity */ function mint( address sender, address recipient, int24 bottomTick, int24 topTick, uint128 amount, bytes calldata data ) external returns ( uint256 amount0, uint256 amount1, uint128 liquidityActual ); /** * @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 bottomTick The lower tick of the position for which to collect fees * @param topTick 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 bottomTick, int24 topTick, 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 bottomTick The lower tick of the position for which to burn liquidity * @param topTick 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 bottomTick, int24 topTick, 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 IAlgebraSwapCallback# AlgebraSwapCallback * @param recipient The address to receive the output of the swap * @param zeroToOne 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 limitSqrtPrice 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. If using the Router it should contain * SwapRouter#SwapCallbackData * @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 zeroToOne, int256 amountSpecified, uint160 limitSqrtPrice, bytes calldata data ) external returns (int256 amount0, int256 amount1); /** * @notice Swap token0 for token1, or token1 for token0 (tokens that have fee on transfer) * @dev The caller of this method receives a callback in the form of I AlgebraSwapCallback# AlgebraSwapCallback * @param sender The address called this function (Comes from the Router) * @param recipient The address to receive the output of the swap * @param zeroToOne 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 limitSqrtPrice 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. If using the Router it should contain * SwapRouter#SwapCallbackData * @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 swapSupportingFeeOnInputTokens( address sender, address recipient, bool zeroToOne, int256 amountSpecified, uint160 limitSqrtPrice, 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 IAlgebraFlashCallback# AlgebraFlashCallback * @dev All excess tokens paid in the callback are distributed to liquidity providers as an additional fee. So this method can be used * to donate underlying tokens 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; }
// 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. * @dev Credit to Uniswap Labs under GPL-2.0-or-later license: * https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces */ interface IAlgebraPoolDerivedState { /** * @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 secondsPerLiquidityCumulatives Cumulative seconds per liquidity-in-range value as of each `secondsAgos` * from the current block timestamp * @return volatilityCumulatives Cumulative standard deviation as of each `secondsAgos` * @return volumePerAvgLiquiditys Cumulative swap volume per liquidity as of each `secondsAgos` */ function getTimepoints(uint32[] calldata secondsAgos) external view returns ( int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, uint112[] memory volatilityCumulatives, uint256[] memory volumePerAvgLiquiditys ); /** * @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 bottomTick The lower tick of the range * @param topTick The upper tick of the range * @return innerTickCumulative The snapshot of the tick accumulator for the range * @return innerSecondsSpentPerLiquidity The snapshot of seconds per liquidity for the range * @return innerSecondsSpent The snapshot of the number of seconds during which the price was in this range */ function getInnerCumulatives(int24 bottomTick, int24 topTick) external view returns ( int56 innerTickCumulative, uint160 innerSecondsSpentPerLiquidity, uint32 innerSecondsSpent ); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Events emitted by a pool /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces interface IAlgebraPoolEvents { /** * @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 price 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 price, 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 bottomTick The lower tick of the position * @param topTick The upper tick of the position * @param liquidityAmount 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 bottomTick, int24 indexed topTick, uint128 liquidityAmount, 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 recipient The address that received fees * @param bottomTick The lower tick of the position * @param topTick 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 bottomTick, int24 indexed topTick, 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 bottomTick The lower tick of the position * @param topTick The upper tick of the position * @param liquidityAmount 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 bottomTick, int24 indexed topTick, uint128 liquidityAmount, 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 price 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 price, 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 when the community fee is changed by the pool * @param communityFee0New The updated value of the token0 community fee percent * @param communityFee1New The updated value of the token1 community fee percent */ event CommunityFee(uint8 communityFee0New, uint8 communityFee1New); /** * @notice Emitted when the tick spacing changes * @param newTickSpacing The updated value of the new tick spacing */ event TickSpacing(int24 newTickSpacing); /** * @notice Emitted when new activeIncentive is set * @param virtualPoolAddress The address of a virtual pool associated with the current active incentive */ event Incentive(address indexed virtualPoolAddress); /** * @notice Emitted when the fee changes * @param fee The value of the token fee */ event Fee(uint16 fee); /** * @notice Emitted when the LiquidityCooldown changes * @param liquidityCooldown The value of locktime for added liquidity */ event LiquidityCooldown(uint32 liquidityCooldown); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import '../IDataStorageOperator.sol'; /// @title Pool state that never changes /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces interface IAlgebraPoolImmutables { /** * @notice The contract that stores all the timepoints and can perform actions with them * @return The operator address */ function dataStorageOperator() external view returns (address); /** * @notice The contract that deployed the pool, which must adhere to the IAlgebraFactory 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 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 Permissioned pool actions * @notice Contains pool methods that may only be called by the factory owner or tokenomics * @dev Credit to Uniswap Labs under GPL-2.0-or-later license: * https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces */ interface IAlgebraPoolPermissionedActions { /** * @notice Set the community's % share of the fees. Cannot exceed 25% (250) * @param communityFee0 new community fee percent for token0 of the pool in thousandths (1e-3) * @param communityFee1 new community fee percent for token1 of the pool in thousandths (1e-3) */ function setCommunityFee(uint8 communityFee0, uint8 communityFee1) external; /// @notice Set the new tick spacing values. Only factory owner /// @param newTickSpacing The new tick spacing value function setTickSpacing(int24 newTickSpacing) external; /** * @notice Sets an active incentive * @param virtualPoolAddress The address of a virtual pool associated with the incentive */ function setIncentive(address virtualPoolAddress) external; /** * @notice Sets new lock time for added liquidity * @param newLiquidityCooldown The time in seconds */ function setLiquidityCooldown(uint32 newLiquidityCooldown) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that can change /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces interface IAlgebraPoolState { /** * @notice The globalState structure in the pool stores many values but requires only one slot * and is exposed as a single method to save gas when accessed externally. * @return price The current price of the pool as a sqrt(token1/token0) Q64.96 value; * Returns tick The current tick of the pool, i.e. according to the last tick transition that was run; * Returns This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(price) if the price is on a tick * boundary; * Returns fee The last pool fee value in hundredths of a bip, i.e. 1e-6; * Returns timepointIndex The index of the last written timepoint; * Returns communityFeeToken0 The community fee percentage of the swap fee in thousandths (1e-3) for token0; * Returns communityFeeToken1 The community fee percentage of the swap fee in thousandths (1e-3) for token1; * Returns unlocked Whether the pool is currently locked to reentrancy; */ function globalState() external view returns ( uint160 price, int24 tick, uint16 fee, uint16 timepointIndex, uint8 communityFeeToken0, uint8 communityFeeToken1, 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 totalFeeGrowth0Token() 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 totalFeeGrowth1Token() external view returns (uint256); /** * @notice The currently in range liquidity available to the pool * @dev This value has no relationship to the total liquidity across all ticks. * Returned value cannot exceed type(uint128).max */ function liquidity() external view returns (uint128); /** * @notice Look up information about a specific tick in the pool * @dev This is a public structure, so the `return` natspec tags are omitted. * @param tick The tick to look up * @return liquidityTotal the total amount of position liquidity that uses the pool either as tick lower or * tick upper; * Returns liquidityDelta how much liquidity changes when the pool price crosses the tick; * Returns outerFeeGrowth0Token the fee growth on the other side of the tick from the current tick in token0; * Returns outerFeeGrowth1Token the fee growth on the other side of the tick from the current tick in token1; * Returns outerTickCumulative the cumulative tick value on the other side of the tick from the current tick; * Returns outerSecondsPerLiquidity the seconds spent per liquidity on the other side of the tick from the current tick; * Returns outerSecondsSpent the seconds spent on the other side of the tick from the current tick; * Returns initialized Set to true if the tick is initialized, i.e. liquidityTotal is greater than 0 * otherwise equal to false. Outside values can only be used if the tick is initialized. * 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 liquidityTotal, int128 liquidityDelta, uint256 outerFeeGrowth0Token, uint256 outerFeeGrowth1Token, int56 outerTickCumulative, uint160 outerSecondsPerLiquidity, uint32 outerSecondsSpent, bool initialized ); /** @notice Returns 256 packed tick initialized boolean values. See TickTable for more information */ function tickTable(int16 wordPosition) external view returns (uint256); /** * @notice Returns the information about a position by the position's key * @dev This is a public mapping of structures, so the `return` natspec tags are omitted. * @param key The position's key is a hash of a preimage composed by the owner, bottomTick and topTick * @return liquidityAmount The amount of liquidity in the position; * Returns lastLiquidityAddTimestamp Timestamp of last adding of liquidity; * Returns innerFeeGrowth0Token Fee growth of token0 inside the tick range as of the last mint/burn/poke; * Returns innerFeeGrowth1Token Fee growth of token1 inside the tick range as of the last mint/burn/poke; * Returns fees0 The computed amount of token0 owed to the position as of the last mint/burn/poke; * Returns fees1 The computed amount of token1 owed to the position as of the last mint/burn/poke */ function positions(bytes32 key) external view returns ( uint128 liquidityAmount, uint32 lastLiquidityAddTimestamp, uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token, uint128 fees0, uint128 fees1 ); /** * @notice Returns data about a specific timepoint index * @param index The element of the timepoints array to fetch * @dev You most likely want to use #getTimepoints() instead of this method to get an timepoint as of some amount of time * ago, rather than at a specific index in the array. * This is a public mapping of structures, so the `return` natspec tags are omitted. * @return initialized whether the timepoint has been initialized and the values are safe to use; * Returns blockTimestamp The timestamp of the timepoint; * Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the timepoint timestamp; * Returns secondsPerLiquidityCumulative the seconds per in range liquidity for the life of the pool as of the timepoint timestamp; * Returns volatilityCumulative Cumulative standard deviation for the life of the pool as of the timepoint timestamp; * Returns averageTick Time-weighted average tick; * Returns volumePerLiquidityCumulative Cumulative swap volume per liquidity for the life of the pool as of the timepoint timestamp; */ function timepoints(uint256 index) external view returns ( bool initialized, uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulative, uint88 volatilityCumulative, int24 averageTick, uint144 volumePerLiquidityCumulative ); /** * @notice Returns the information about active incentive * @dev if there is no active incentive at the moment, virtualPool,endTimestamp,startTimestamp would be equal to 0 * @return virtualPool The address of a virtual pool associated with the current active incentive */ function activeIncentive() external view returns (address virtualPool); /** * @notice Returns the lock time for added liquidity */ function liquidityCooldown() external view returns (uint32 cooldownInSeconds); /** * @notice The pool tick spacing * @dev Ticks can only be used at multiples of this value * e.g.: a tickSpacing of 60 means ticks can be initialized every 60th tick, i.e., ..., -120, -60, 0, 60, 120, ... * This value is an int24 to avoid casting even though it is always positive. * @return The tick spacing */ function tickSpacing() external view returns (int24); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import './Constants.sol'; /// @title AdaptiveFee /// @notice Calculates fee based on combination of sigmoids library AdaptiveFee { // alpha1 + alpha2 + baseFee must be <= type(uint16).max struct Configuration { uint16 alpha1; // max value of the first sigmoid uint16 alpha2; // max value of the second sigmoid uint32 beta1; // shift along the x-axis for the first sigmoid uint32 beta2; // shift along the x-axis for the second sigmoid uint16 gamma1; // horizontal stretch factor for the first sigmoid uint16 gamma2; // horizontal stretch factor for the second sigmoid uint32 volumeBeta; // shift along the x-axis for the outer volume-sigmoid uint16 volumeGamma; // horizontal stretch factor the outer volume-sigmoid uint16 baseFee; // minimum possible fee } /// @notice Calculates fee based on formula: /// baseFee + sigmoidVolume(sigmoid1(volatility, volumePerLiquidity) + sigmoid2(volatility, volumePerLiquidity)) /// maximum value capped by baseFee + alpha1 + alpha2 function getFee( uint88 volatility, uint256 volumePerLiquidity, Configuration memory config ) internal pure returns (uint16 fee) { uint256 sumOfSigmoids = sigmoid(volatility, config.gamma1, config.alpha1, config.beta1) + sigmoid(volatility, config.gamma2, config.alpha2, config.beta2); if (sumOfSigmoids > type(uint16).max) { // should be impossible, just in case sumOfSigmoids = type(uint16).max; } return uint16(config.baseFee + sigmoid(volumePerLiquidity, config.volumeGamma, uint16(sumOfSigmoids), config.volumeBeta)); // safe since alpha1 + alpha2 + baseFee _must_ be <= type(uint16).max } /// @notice calculates α / (1 + e^( (β-x) / γ)) /// that is a sigmoid with a maximum value of α, x-shifted by β, and stretched by γ /// @dev returns uint256 for fuzzy testing. Guaranteed that the result is not greater than alpha function sigmoid( uint256 x, uint16 g, uint16 alpha, uint256 beta ) internal pure returns (uint256 res) { if (x > beta) { x = x - beta; if (x >= 6 * uint256(g)) return alpha; // so x < 19 bits uint256 g8 = uint256(g)**8; // < 128 bits (8*16) uint256 ex = exp(x, g, g8); // < 155 bits res = (alpha * ex) / (g8 + ex); // in worst case: (16 + 155 bits) / 155 bits // so res <= alpha } else { x = beta - x; if (x >= 6 * uint256(g)) return 0; // so x < 19 bits uint256 g8 = uint256(g)**8; // < 128 bits (8*16) uint256 ex = g8 + exp(x, g, g8); // < 156 bits res = (alpha * g8) / ex; // in worst case: (16 + 128 bits) / 156 bits // g8 <= ex, so res <= alpha } } /// @notice calculates e^(x/g) * g^8 in a series, since (around zero): /// e^x = 1 + x + x^2/2 + ... + x^n/n! + ... /// e^(x/g) = 1 + x/g + x^2/(2*g^2) + ... + x^(n)/(g^n * n!) + ... function exp( uint256 x, uint16 g, uint256 gHighestDegree ) internal pure returns (uint256 res) { // calculating: // g**8 + x * g**7 + (x**2 * g**6) / 2 + (x**3 * g**5) / 6 + (x**4 * g**4) / 24 + (x**5 * g**3) / 120 + (x**6 * g^2) / 720 + x**7 * g / 5040 + x**8 / 40320 // x**8 < 152 bits (19*8) and g**8 < 128 bits (8*16) // so each summand < 152 bits and res < 155 bits uint256 xLowestDegree = x; res = gHighestDegree; // g**8 gHighestDegree /= g; // g**7 res += xLowestDegree * gHighestDegree; gHighestDegree /= g; // g**6 xLowestDegree *= x; // x**2 res += (xLowestDegree * gHighestDegree) / 2; gHighestDegree /= g; // g**5 xLowestDegree *= x; // x**3 res += (xLowestDegree * gHighestDegree) / 6; gHighestDegree /= g; // g**4 xLowestDegree *= x; // x**4 res += (xLowestDegree * gHighestDegree) / 24; gHighestDegree /= g; // g**3 xLowestDegree *= x; // x**5 res += (xLowestDegree * gHighestDegree) / 120; gHighestDegree /= g; // g**2 xLowestDegree *= x; // x**6 res += (xLowestDegree * gHighestDegree) / 720; xLowestDegree *= x; // x**7 res += (xLowestDegree * g) / 5040 + (xLowestDegree * x) / (40320); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity =0.7.6; library Constants { uint8 internal constant RESOLUTION = 96; uint256 internal constant Q96 = 0x1000000000000000000000000; uint256 internal constant Q128 = 0x100000000000000000000000000000000; // fee value in hundredths of a bip, i.e. 1e-6 uint16 internal constant BASE_FEE = 100; int24 internal constant MAX_TICK_SPACING = 500; // max(uint128) / (MAX_TICK - MIN_TICK) uint128 internal constant MAX_LIQUIDITY_PER_TICK = 191757638537527648490752896198553; uint32 internal constant MAX_LIQUIDITY_COOLDOWN = 1 days; uint8 internal constant MAX_COMMUNITY_FEE = 250; uint256 internal constant COMMUNITY_FEE_DENOMINATOR = 1000; }
// SPDX-License-Identifier: MIT pragma solidity ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.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) { // 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 = a * b; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(a, b, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Make sure the result is less than 2**256. // Also prevents denominator == 0 require(denominator > prod1); // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { assembly { result := div(prod0, denominator) } return result; } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0] // Compute remainder using mulmod // Subtract 256 bit remainder from 512 bit number assembly { let remainder := mulmod(a, b, denominator) 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 = -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 preconditions 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) { if (a == 0 || ((result = a * b) / a == b)) { require(denominator > 0); assembly { result := add(div(result, denominator), gt(mod(result, denominator), 0)) } } else { result = mulDiv(a, b, denominator); if (mulmod(a, b, denominator) > 0) { require(result < type(uint256).max); result++; } } } /// @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)) } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Math library for liquidity /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries library LiquidityMath { /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows /// @param x The liquidity before change /// @param y The delta by which liquidity should be changed /// @return z The liquidity delta function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { if (y < 0) { require((z = x - uint128(-y)) < x, 'LS'); } else { require((z = x + uint128(y)) >= x, 'LA'); } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.0; /// @title Optimized overflow and underflow safe math operations /// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries library LowGasSafeMath { /// @notice Returns x + y, reverts if sum overflows uint256 /// @param x The augend /// @param y The addend /// @return z The sum of x and y function add(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x + y) >= x); } /// @notice Returns x - y, reverts if underflows /// @param x The minuend /// @param y The subtrahend /// @return z The difference of x and y function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x - y) <= x); } /// @notice Returns x * y, reverts if overflows /// @param x The multiplicand /// @param y The multiplier /// @return z The product of x and y function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { require(x == 0 || (z = x * y) / x == y); } /// @notice Returns x + y, reverts if overflows or underflows /// @param x The augend /// @param y The addend /// @return z The sum of x and y function add(int256 x, int256 y) internal pure returns (int256 z) { require((z = x + y) >= x == (y >= 0)); } /// @notice Returns x - y, reverts if overflows or underflows /// @param x The minuend /// @param y The subtrahend /// @return z The difference of x and y function sub(int256 x, int256 y) internal pure returns (int256 z) { require((z = x - y) <= x == (y >= 0)); } /// @notice Returns x + y, reverts if overflows or underflows /// @param x The augend /// @param y The addend /// @return z The sum of x and y function add128(uint128 x, uint128 y) internal pure returns (uint128 z) { require((z = x + y) >= x); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import './FullMath.sol'; import './TokenDeltaMath.sol'; /// @title Computes the result of price movement /// @notice Contains methods for computing the result of price movement within a single tick price range. library PriceMovementMath { using LowGasSafeMath for uint256; using SafeCast for uint256; /// @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 price The starting Q64.96 sqrt price, i.e., before accounting for the input amount /// @param liquidity The amount of usable liquidity /// @param input How much of token0, or token1, is being swapped in /// @param zeroToOne Whether the amount in is token0 or token1 /// @return resultPrice The Q64.96 sqrt price after adding the input amount to token0 or token1 function getNewPriceAfterInput( uint160 price, uint128 liquidity, uint256 input, bool zeroToOne ) internal pure returns (uint160 resultPrice) { return getNewPrice(price, liquidity, input, zeroToOne, 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 price The starting Q64.96 sqrt price before accounting for the output amount /// @param liquidity The amount of usable liquidity /// @param output How much of token0, or token1, is being swapped out /// @param zeroToOne Whether the amount out is token0 or token1 /// @return resultPrice The Q64.96 sqrt price after removing the output amount of token0 or token1 function getNewPriceAfterOutput( uint160 price, uint128 liquidity, uint256 output, bool zeroToOne ) internal pure returns (uint160 resultPrice) { return getNewPrice(price, liquidity, output, zeroToOne, false); } function getNewPrice( uint160 price, uint128 liquidity, uint256 amount, bool zeroToOne, bool fromInput ) internal pure returns (uint160 resultPrice) { require(price > 0); require(liquidity > 0); if (zeroToOne == fromInput) { // rounding up or down if (amount == 0) return price; uint256 liquidityShifted = uint256(liquidity) << Constants.RESOLUTION; if (fromInput) { uint256 product; if ((product = amount * price) / amount == price) { uint256 denominator = liquidityShifted + product; if (denominator >= liquidityShifted) return uint160(FullMath.mulDivRoundingUp(liquidityShifted, price, denominator)); // always fits in 160 bits } return uint160(FullMath.divRoundingUp(liquidityShifted, (liquidityShifted / price).add(amount))); } else { uint256 product; require((product = amount * price) / amount == price); // if the product overflows, we know the denominator underflows require(liquidityShifted > product); // in addition, we must check that the denominator does not underflow return FullMath.mulDivRoundingUp(liquidityShifted, price, liquidityShifted - product).toUint160(); } } else { // if we're adding (subtracting), rounding down requires rounding the quotient down (up) // in both cases, avoid a mulDiv for most inputs if (fromInput) { return uint256(price) .add(amount <= type(uint160).max ? (amount << Constants.RESOLUTION) / liquidity : FullMath.mulDiv(amount, Constants.Q96, liquidity)) .toUint160(); } else { uint256 quotient = amount <= type(uint160).max ? FullMath.divRoundingUp(amount << Constants.RESOLUTION, liquidity) : FullMath.mulDivRoundingUp(amount, Constants.Q96, liquidity); require(price > quotient); return uint160(price - quotient); // always fits 160 bits } } } function getTokenADelta01( uint160 to, uint160 from, uint128 liquidity ) internal pure returns (uint256) { return TokenDeltaMath.getToken0Delta(to, from, liquidity, true); } function getTokenADelta10( uint160 to, uint160 from, uint128 liquidity ) internal pure returns (uint256) { return TokenDeltaMath.getToken1Delta(from, to, liquidity, true); } function getTokenBDelta01( uint160 to, uint160 from, uint128 liquidity ) internal pure returns (uint256) { return TokenDeltaMath.getToken1Delta(to, from, liquidity, false); } function getTokenBDelta10( uint160 to, uint160 from, uint128 liquidity ) internal pure returns (uint256) { return TokenDeltaMath.getToken0Delta(from, to, liquidity, false); } /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive /// @param currentPrice The current Q64.96 sqrt price of the pool /// @param targetPrice The Q64.96 sqrt price that cannot be exceeded, from which the direction of the swap is inferred /// @param liquidity The usable liquidity /// @param amountAvailable How much input or output amount is remaining to be swapped in/out /// @param fee The fee taken from the input amount, expressed in hundredths of a bip /// @return resultPrice The Q64.96 sqrt price after swapping the amount in/out, not to exceed the price target /// @return input The amount to be swapped in, of either token0 or token1, based on the direction of the swap /// @return output The amount to be received, of either token0 or token1, based on the direction of the swap /// @return feeAmount The amount of input that will be taken as a fee function movePriceTowardsTarget( bool zeroToOne, uint160 currentPrice, uint160 targetPrice, uint128 liquidity, int256 amountAvailable, uint16 fee ) internal pure returns ( uint160 resultPrice, uint256 input, uint256 output, uint256 feeAmount ) { function(uint160, uint160, uint128) pure returns (uint256) getAmountA = zeroToOne ? getTokenADelta01 : getTokenADelta10; if (amountAvailable >= 0) { // exactIn or not uint256 amountAvailableAfterFee = FullMath.mulDiv(uint256(amountAvailable), 1e6 - fee, 1e6); input = getAmountA(targetPrice, currentPrice, liquidity); if (amountAvailableAfterFee >= input) { resultPrice = targetPrice; feeAmount = FullMath.mulDivRoundingUp(input, fee, 1e6 - fee); } else { resultPrice = getNewPriceAfterInput(currentPrice, liquidity, amountAvailableAfterFee, zeroToOne); if (targetPrice != resultPrice) { input = getAmountA(resultPrice, currentPrice, liquidity); // we didn't reach the target, so take the remainder of the maximum input as fee feeAmount = uint256(amountAvailable) - input; } else { feeAmount = FullMath.mulDivRoundingUp(input, fee, 1e6 - fee); } } output = (zeroToOne ? getTokenBDelta01 : getTokenBDelta10)(resultPrice, currentPrice, liquidity); } else { function(uint160, uint160, uint128) pure returns (uint256) getAmountB = zeroToOne ? getTokenBDelta01 : getTokenBDelta10; output = getAmountB(targetPrice, currentPrice, liquidity); amountAvailable = -amountAvailable; if (uint256(amountAvailable) >= output) resultPrice = targetPrice; else { resultPrice = getNewPriceAfterOutput(currentPrice, liquidity, uint256(amountAvailable), zeroToOne); if (targetPrice != resultPrice) { output = getAmountB(resultPrice, currentPrice, liquidity); } // cap the output amount to not exceed the remaining output amount if (output > uint256(amountAvailable)) { output = uint256(amountAvailable); } } input = getAmountA(resultPrice, currentPrice, liquidity); feeAmount = FullMath.mulDivRoundingUp(input, fee, 1e6 - fee); } } }
// 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 /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries 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: BUSL-1.1 pragma solidity =0.7.6; import './LowGasSafeMath.sol'; import './SafeCast.sol'; import './LiquidityMath.sol'; import './Constants.sol'; /// @title TickManager /// @notice Contains functions for managing tick processes and relevant calculations library TickManager { using LowGasSafeMath for int256; using SafeCast for int256; // info stored for each initialized individual tick struct Tick { uint128 liquidityTotal; // the total position liquidity that references this tick int128 liquidityDelta; // amount of net liquidity added (subtracted) when tick is crossed left-right (right-left), // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) // only has relative meaning, not absolute — the value depends on when the tick is initialized uint256 outerFeeGrowth0Token; uint256 outerFeeGrowth1Token; int56 outerTickCumulative; // the cumulative tick value on the other side of the tick uint160 outerSecondsPerLiquidity; // the seconds per unit of liquidity on the _other_ side of current tick, (relative meaning) uint32 outerSecondsSpent; // the seconds spent on the other side of the current tick, only has relative meaning bool initialized; // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks } /// @notice Retrieves fee growth data /// @param self The mapping containing all tick information for initialized ticks /// @param bottomTick The lower tick boundary of the position /// @param topTick The upper tick boundary of the position /// @param currentTick The current tick /// @param totalFeeGrowth0Token The all-time global fee growth, per unit of liquidity, in token0 /// @param totalFeeGrowth1Token The all-time global fee growth, per unit of liquidity, in token1 /// @return innerFeeGrowth0Token The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries /// @return innerFeeGrowth1Token The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries function getInnerFeeGrowth( mapping(int24 => Tick) storage self, int24 bottomTick, int24 topTick, int24 currentTick, uint256 totalFeeGrowth0Token, uint256 totalFeeGrowth1Token ) internal view returns (uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token) { Tick storage lower = self[bottomTick]; Tick storage upper = self[topTick]; if (currentTick < topTick) { if (currentTick >= bottomTick) { innerFeeGrowth0Token = totalFeeGrowth0Token - lower.outerFeeGrowth0Token; innerFeeGrowth1Token = totalFeeGrowth1Token - lower.outerFeeGrowth1Token; } else { innerFeeGrowth0Token = lower.outerFeeGrowth0Token; innerFeeGrowth1Token = lower.outerFeeGrowth1Token; } innerFeeGrowth0Token -= upper.outerFeeGrowth0Token; innerFeeGrowth1Token -= upper.outerFeeGrowth1Token; } else { innerFeeGrowth0Token = upper.outerFeeGrowth0Token - lower.outerFeeGrowth0Token; innerFeeGrowth1Token = upper.outerFeeGrowth1Token - lower.outerFeeGrowth1Token; } } /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa /// @param self The mapping containing all tick information for initialized ticks /// @param tick The tick that will be updated /// @param currentTick The current tick /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left) /// @param totalFeeGrowth0Token The all-time global fee growth, per unit of liquidity, in token0 /// @param totalFeeGrowth1Token The all-time global fee growth, per unit of liquidity, in token1 /// @param secondsPerLiquidityCumulative The all-time seconds per max(1, liquidity) of the pool /// @param tickCumulative The all-time global cumulative tick /// @param time The current block timestamp cast to a uint32 /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa function update( mapping(int24 => Tick) storage self, int24 tick, int24 currentTick, int128 liquidityDelta, uint256 totalFeeGrowth0Token, uint256 totalFeeGrowth1Token, uint160 secondsPerLiquidityCumulative, int56 tickCumulative, uint32 time, bool upper ) internal returns (bool flipped) { Tick storage data = self[tick]; int128 liquidityDeltaBefore = data.liquidityDelta; uint128 liquidityTotalBefore = data.liquidityTotal; uint128 liquidityTotalAfter = LiquidityMath.addDelta(liquidityTotalBefore, liquidityDelta); require(liquidityTotalAfter < Constants.MAX_LIQUIDITY_PER_TICK + 1, 'LO'); // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) data.liquidityDelta = upper ? int256(liquidityDeltaBefore).sub(liquidityDelta).toInt128() : int256(liquidityDeltaBefore).add(liquidityDelta).toInt128(); data.liquidityTotal = liquidityTotalAfter; flipped = (liquidityTotalAfter == 0); if (liquidityTotalBefore == 0) { flipped = !flipped; // by convention, we assume that all growth before a tick was initialized happened _below_ the tick if (tick <= currentTick) { data.outerFeeGrowth0Token = totalFeeGrowth0Token; data.outerFeeGrowth1Token = totalFeeGrowth1Token; data.outerSecondsPerLiquidity = secondsPerLiquidityCumulative; data.outerTickCumulative = tickCumulative; data.outerSecondsSpent = time; } data.initialized = true; } } /// @notice Transitions to next tick as needed by price movement /// @param self The mapping containing all tick information for initialized ticks /// @param tick The destination tick of the transition /// @param totalFeeGrowth0Token The all-time global fee growth, per unit of liquidity, in token0 /// @param totalFeeGrowth1Token The all-time global fee growth, per unit of liquidity, in token1 /// @param secondsPerLiquidityCumulative The current seconds per liquidity /// @param tickCumulative The all-time global cumulative tick /// @param time The current block.timestamp /// @return liquidityDelta The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) function cross( mapping(int24 => Tick) storage self, int24 tick, uint256 totalFeeGrowth0Token, uint256 totalFeeGrowth1Token, uint160 secondsPerLiquidityCumulative, int56 tickCumulative, uint32 time ) internal returns (int128 liquidityDelta) { Tick storage data = self[tick]; data.outerSecondsSpent = time - data.outerSecondsSpent; data.outerSecondsPerLiquidity = secondsPerLiquidityCumulative - data.outerSecondsPerLiquidity; data.outerTickCumulative = tickCumulative - data.outerTickCumulative; data.outerFeeGrowth1Token = totalFeeGrowth1Token - data.outerFeeGrowth1Token; data.outerFeeGrowth0Token = totalFeeGrowth0Token - data.outerFeeGrowth0Token; return data.liquidityDelta; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.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 /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries library TickMath { /// @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 price 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 price) { // get abs value int24 mask = tick >> (24 - 1); uint256 absTick = uint256((tick ^ mask) - mask); require(absTick <= uint256(MAX_TICK), '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 price = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); } /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio /// @dev Throws in case price < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may /// ever return. /// @param price 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 price) internal pure returns (int24 tick) { // second inequality must be < because the price can never reach the price at the max tick require(price >= MIN_SQRT_RATIO && price < MAX_SQRT_RATIO, 'R'); uint256 ratio = uint256(price) << 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) <= price ? tickHi : tickLow; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import './TickMath.sol'; /// @title Packed tick initialized state library /// @notice Stores a packed mapping of tick index to its initialized state /// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word. library TickTable { /// @notice Toggles the initialized state for a given tick from false to true, or vice versa /// @param self The mapping in which to toggle the tick /// @param tick The tick to toggle function toggleTick(mapping(int16 => uint256) storage self, int24 tick) internal { int16 rowNumber; uint8 bitNumber; assembly { bitNumber := and(tick, 0xFF) rowNumber := shr(8, tick) } self[rowNumber] ^= 1 << bitNumber; } /// @notice get position of single 1-bit /// @dev it is assumed that word contains exactly one 1-bit, otherwise the result will be incorrect /// @param word The word containing only one 1-bit function getSingleSignificantBit(uint256 word) internal pure returns (uint8 singleBitPos) { assembly { singleBitPos := iszero(and(word, 0x5555555555555555555555555555555555555555555555555555555555555555)) singleBitPos := or(singleBitPos, shl(7, iszero(and(word, 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)))) singleBitPos := or(singleBitPos, shl(6, iszero(and(word, 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF)))) singleBitPos := or(singleBitPos, shl(5, iszero(and(word, 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF)))) singleBitPos := or(singleBitPos, shl(4, iszero(and(word, 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF)))) singleBitPos := or(singleBitPos, shl(3, iszero(and(word, 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF)))) singleBitPos := or(singleBitPos, shl(2, iszero(and(word, 0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F)))) singleBitPos := or(singleBitPos, shl(1, iszero(and(word, 0x3333333333333333333333333333333333333333333333333333333333333333)))) } } /// @notice get position of most significant 1-bit (leftmost) /// @dev it is assumed that before the call, a check will be made that the argument (word) is not equal to zero /// @param word The word containing at least one 1-bit function getMostSignificantBit(uint256 word) internal pure returns (uint8 mostBitPos) { assembly { word := or(word, shr(1, word)) word := or(word, shr(2, word)) word := or(word, shr(4, word)) word := or(word, shr(8, word)) word := or(word, shr(16, word)) word := or(word, shr(32, word)) word := or(word, shr(64, word)) word := or(word, shr(128, word)) word := sub(word, shr(1, word)) } return (getSingleSignificantBit(word)); } /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either /// to the left (less than or equal to) or right (greater than) of the given tick /// @param self The mapping in which to compute the next initialized tick /// @param tick The starting tick /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick) /// @return nextTick The next initialized or uninitialized tick up to 256 ticks away from the current tick /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks function nextTickInTheSameRow( mapping(int16 => uint256) storage self, int24 tick, bool lte ) internal view returns (int24 nextTick, bool initialized) { if (lte) { // unpacking not made into a separate function for gas and contract size savings int16 rowNumber; uint8 bitNumber; assembly { bitNumber := and(tick, 0xFF) rowNumber := shr(8, tick) } uint256 _row = self[rowNumber] << (255 - bitNumber); // all the 1s at or to the right of the current bitNumber if (_row != 0) { tick -= int24(255 - getMostSignificantBit(_row)); return (boundTick(tick), true); } else { tick -= int24(bitNumber); return (boundTick(tick), false); } } else { // start from the word of the next tick, since the current tick state doesn't matter tick += 1; int16 rowNumber; uint8 bitNumber; assembly { bitNumber := and(tick, 0xFF) rowNumber := shr(8, tick) } // all the 1s at or to the left of the bitNumber uint256 _row = self[rowNumber] >> (bitNumber); if (_row != 0) { tick += int24(getSingleSignificantBit(-_row & _row)); // least significant bit return (boundTick(tick), true); } else { tick += int24(255 - bitNumber); return (boundTick(tick), false); } } } function boundTick(int24 tick) private pure returns (int24 boundedTick) { boundedTick = tick; if (boundedTick < TickMath.MIN_TICK) { boundedTick = TickMath.MIN_TICK; } else if (boundedTick > TickMath.MAX_TICK) { boundedTick = TickMath.MAX_TICK; } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import './LowGasSafeMath.sol'; import './SafeCast.sol'; import './FullMath.sol'; import './Constants.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 TokenDeltaMath { using LowGasSafeMath for uint256; using SafeCast for uint256; /// @notice Gets the token0 delta between two prices /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper) /// @param priceLower A Q64.96 sqrt price /// @param priceUpper Another Q64.96 sqrt price /// @param liquidity The amount of usable liquidity /// @param roundUp Whether to round the amount up or down /// @return token0Delta Amount of token0 required to cover a position of size liquidity between the two passed prices function getToken0Delta( uint160 priceLower, uint160 priceUpper, uint128 liquidity, bool roundUp ) internal pure returns (uint256 token0Delta) { uint256 priceDelta = priceUpper - priceLower; require(priceDelta < priceUpper); // forbids underflow and 0 priceLower uint256 liquidityShifted = uint256(liquidity) << Constants.RESOLUTION; token0Delta = roundUp ? FullMath.divRoundingUp(FullMath.mulDivRoundingUp(priceDelta, liquidityShifted, priceUpper), priceLower) : FullMath.mulDiv(priceDelta, liquidityShifted, priceUpper) / priceLower; } /// @notice Gets the token1 delta between two prices /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) /// @param priceLower A Q64.96 sqrt price /// @param priceUpper Another Q64.96 sqrt price /// @param liquidity The amount of usable liquidity /// @param roundUp Whether to round the amount up, or down /// @return token1Delta Amount of token1 required to cover a position of size liquidity between the two passed prices function getToken1Delta( uint160 priceLower, uint160 priceUpper, uint128 liquidity, bool roundUp ) internal pure returns (uint256 token1Delta) { require(priceUpper >= priceLower); uint256 priceDelta = priceUpper - priceLower; token1Delta = roundUp ? FullMath.mulDivRoundingUp(priceDelta, liquidity, Constants.Q96) : FullMath.mulDiv(priceDelta, liquidity, Constants.Q96); } /// @notice Helper that gets signed token0 delta /// @param priceLower A Q64.96 sqrt price /// @param priceUpper Another Q64.96 sqrt price /// @param liquidity The change in liquidity for which to compute the token0 delta /// @return token0Delta Amount of token0 corresponding to the passed liquidityDelta between the two prices function getToken0Delta( uint160 priceLower, uint160 priceUpper, int128 liquidity ) internal pure returns (int256 token0Delta) { token0Delta = liquidity >= 0 ? getToken0Delta(priceLower, priceUpper, uint128(liquidity), true).toInt256() : -getToken0Delta(priceLower, priceUpper, uint128(-liquidity), false).toInt256(); } /// @notice Helper that gets signed token1 delta /// @param priceLower A Q64.96 sqrt price /// @param priceUpper Another Q64.96 sqrt price /// @param liquidity The change in liquidity for which to compute the token1 delta /// @return token1Delta Amount of token1 corresponding to the passed liquidityDelta between the two prices function getToken1Delta( uint160 priceLower, uint160 priceUpper, int128 liquidity ) internal pure returns (int256 token1Delta) { token1Delta = liquidity >= 0 ? getToken1Delta(priceLower, priceUpper, uint128(liquidity), true).toInt256() : -getToken1Delta(priceLower, priceUpper, uint128(-liquidity), false).toInt256(); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.6.0; import '../interfaces/IERC20Minimal.sol'; /// @title TransferHelper /// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries library TransferHelper { /// @notice Transfers tokens from msg.sender to a recipient /// @dev Calls transfer on token contract, errors with TF if transfer fails /// @param token The contract address of the token which will be transferred /// @param to The recipient of the transfer /// @param value The value of the transfer function safeTransfer( address token, address to, uint256 value ) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); } }
{ "optimizer": { "enabled": true, "runs": 0 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"int24","name":"bottomTick","type":"int24"},{"indexed":true,"internalType":"int24","name":"topTick","type":"int24"},{"indexed":false,"internalType":"uint128","name":"liquidityAmount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"int24","name":"bottomTick","type":"int24"},{"indexed":true,"internalType":"int24","name":"topTick","type":"int24"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"Collect","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"communityFee0New","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"communityFee1New","type":"uint8"}],"name":"CommunityFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"fee","type":"uint16"}],"name":"Fee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid1","type":"uint256"}],"name":"Flash","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"virtualPoolAddress","type":"address"}],"name":"Incentive","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint160","name":"price","type":"uint160"},{"indexed":false,"internalType":"int24","name":"tick","type":"int24"}],"name":"Initialize","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"liquidityCooldown","type":"uint32"}],"name":"LiquidityCooldown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"int24","name":"bottomTick","type":"int24"},{"indexed":true,"internalType":"int24","name":"topTick","type":"int24"},{"indexed":false,"internalType":"uint128","name":"liquidityAmount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"int256","name":"amount0","type":"int256"},{"indexed":false,"internalType":"int256","name":"amount1","type":"int256"},{"indexed":false,"internalType":"uint160","name":"price","type":"uint160"},{"indexed":false,"internalType":"uint128","name":"liquidity","type":"uint128"},{"indexed":false,"internalType":"int24","name":"tick","type":"int24"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int24","name":"newTickSpacing","type":"int24"}],"name":"TickSpacing","type":"event"},{"inputs":[],"name":"activeIncentive","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"bottomTick","type":"int24"},{"internalType":"int24","name":"topTick","type":"int24"},{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"int24","name":"bottomTick","type":"int24"},{"internalType":"int24","name":"topTick","type":"int24"},{"internalType":"uint128","name":"amount0Requested","type":"uint128"},{"internalType":"uint128","name":"amount1Requested","type":"uint128"}],"name":"collect","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dataStorageOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int24","name":"bottomTick","type":"int24"},{"internalType":"int24","name":"topTick","type":"int24"}],"name":"getInnerCumulatives","outputs":[{"internalType":"int56","name":"innerTickCumulative","type":"int56"},{"internalType":"uint160","name":"innerSecondsSpentPerLiquidity","type":"uint160"},{"internalType":"uint32","name":"innerSecondsSpent","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"secondsAgos","type":"uint32[]"}],"name":"getTimepoints","outputs":[{"internalType":"int56[]","name":"tickCumulatives","type":"int56[]"},{"internalType":"uint160[]","name":"secondsPerLiquidityCumulatives","type":"uint160[]"},{"internalType":"uint112[]","name":"volatilityCumulatives","type":"uint112[]"},{"internalType":"uint256[]","name":"volumePerAvgLiquiditys","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalState","outputs":[{"internalType":"uint160","name":"price","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint16","name":"fee","type":"uint16"},{"internalType":"uint16","name":"timepointIndex","type":"uint16"},{"internalType":"uint8","name":"communityFeeToken0","type":"uint8"},{"internalType":"uint8","name":"communityFeeToken1","type":"uint8"},{"internalType":"bool","name":"unlocked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint160","name":"initialPrice","type":"uint160"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidity","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityCooldown","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLiquidityPerTick","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"int24","name":"bottomTick","type":"int24"},{"internalType":"int24","name":"topTick","type":"int24"},{"internalType":"uint128","name":"liquidityDesired","type":"uint128"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mint","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint128","name":"liquidityActual","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"positions","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint32","name":"lastLiquidityAddTimestamp","type":"uint32"},{"internalType":"uint256","name":"innerFeeGrowth0Token","type":"uint256"},{"internalType":"uint256","name":"innerFeeGrowth1Token","type":"uint256"},{"internalType":"uint128","name":"fees0","type":"uint128"},{"internalType":"uint128","name":"fees1","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"communityFee0","type":"uint8"},{"internalType":"uint8","name":"communityFee1","type":"uint8"}],"name":"setCommunityFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"virtualPoolAddress","type":"address"}],"name":"setIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newLiquidityCooldown","type":"uint32"}],"name":"setLiquidityCooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int24","name":"newTickSpacing","type":"int24"}],"name":"setTickSpacing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"zeroToOne","type":"bool"},{"internalType":"int256","name":"amountRequired","type":"int256"},{"internalType":"uint160","name":"limitSqrtPrice","type":"uint160"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"int256","name":"amount0","type":"int256"},{"internalType":"int256","name":"amount1","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"zeroToOne","type":"bool"},{"internalType":"int256","name":"amountRequired","type":"int256"},{"internalType":"uint160","name":"limitSqrtPrice","type":"uint160"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapSupportingFeeOnInputTokens","outputs":[{"internalType":"int256","name":"amount0","type":"int256"},{"internalType":"int256","name":"amount1","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tickSpacing","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int16","name":"","type":"int16"}],"name":"tickTable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"","type":"int24"}],"name":"ticks","outputs":[{"internalType":"uint128","name":"liquidityTotal","type":"uint128"},{"internalType":"int128","name":"liquidityDelta","type":"int128"},{"internalType":"uint256","name":"outerFeeGrowth0Token","type":"uint256"},{"internalType":"uint256","name":"outerFeeGrowth1Token","type":"uint256"},{"internalType":"int56","name":"outerTickCumulative","type":"int56"},{"internalType":"uint160","name":"outerSecondsPerLiquidity","type":"uint160"},{"internalType":"uint32","name":"outerSecondsSpent","type":"uint32"},{"internalType":"bool","name":"initialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"timepoints","outputs":[{"internalType":"bool","name":"initialized","type":"bool"},{"internalType":"uint32","name":"blockTimestamp","type":"uint32"},{"internalType":"int56","name":"tickCumulative","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityCumulative","type":"uint160"},{"internalType":"uint88","name":"volatilityCumulative","type":"uint88"},{"internalType":"int24","name":"averageTick","type":"int24"},{"internalType":"uint144","name":"volumePerLiquidityCumulative","type":"uint144"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFeeGrowth0Token","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFeeGrowth1Token","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061015f5760003560e01c80630dfe168114610164578063128acb081461018857806317e25b3c146102355780631a68650214610256578063289fe9b01461027a57806329047dfa1461029f578063490e6cbc146102a75780634f1eb3d814610331578063514ea4bf146103b15780636378ae441461041657806370cf754a14610430578063713346941461043857806374eceae6146104d55780637c0112b7146105515780637c1fe0c814610579578063920c34e51461059f5780639d3a5241146105fa578063a34123a71461078b578063aafe29c0146107c5578063c45a015514610897578063c677e3e01461089f578063d0c93a7c146108bf578063d21220a7146108de578063e76c01e4146108e6578063ecdecf4214610942578063f085a6101461094a578063f30dba931461096a578063f637731d146109ec578063facb0eb114610a12575b600080fd5b61016c610a1a565b604080516001600160a01b039092168252519081900360200190f35b61021c600480360360a081101561019e57600080fd5b6001600160a01b0382358116926020810135151592604082013592606083013516919081019060a081016080820135600160201b8111156101de57600080fd5b8201836020820111156101f057600080fd5b803590602001918460018302840111600160201b8311171561021157600080fd5b509092509050610a3e565b6040805192835260208301919091528051918290030190f35b61023d610c7a565b6040805163ffffffff9092168252519081900360200190f35b61025e610c86565b604080516001600160801b039092168252519081900360200190f35b61029d6004803603602081101561029057600080fd5b503563ffffffff16610c95565b005b61016c610da9565b61029d600480360360808110156102bd57600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b8111156102f357600080fd5b82018360208201111561030557600080fd5b803590602001918460018302840111600160201b8311171561032657600080fd5b509092509050610dcd565b610382600480360360a081101561034757600080fd5b506001600160a01b03813516906020810135600290810b91604081013590910b906001600160801b036060820135811691608001351661124d565b60405180836001600160801b03168152602001826001600160801b031681526020019250505060405180910390f35b6103ce600480360360208110156103c757600080fd5b5035611447565b604080516001600160801b03978816815263ffffffff90961660208701528581019490945260608501929092528416608084015290921660a082015290519081900360c00190f35b61041e611490565b60408051918252519081900360200190f35b61025e611496565b61021c600480360360c081101561044e57600080fd5b6001600160a01b0382358116926020810135821692604082013515159260608301359260808101359091169181019060c0810160a0820135600160201b81111561049757600080fd5b8201836020820111156104a957600080fd5b803590602001918460018302840111600160201b831117156104ca57600080fd5b5090925090506114a8565b6104f2600480360360208110156104eb57600080fd5b50356117a4565b60408051971515885263ffffffff909616602088015260069490940b868601526001600160a01b0390921660608601526001600160581b0316608085015260020b60a08401526001600160901b031660c0830152519081900360e00190f35b61029d6004803603604081101561056757600080fd5b5060ff81358116916020013516611875565b61029d6004803603602081101561058f57600080fd5b50356001600160a01b0316611a0c565b6105c9600480360360408110156105b557600080fd5b508035600290810b9160200135900b611afb565b6040805160069490940b84526001600160a01b03909216602084015263ffffffff1682820152519081900360600190f35b6106686004803603602081101561061057600080fd5b810190602081018135600160201b81111561062a57600080fd5b82018360208201111561063c57600080fd5b803590602001918460208302840111600160201b8311171561065d57600080fd5b509092509050611db2565b6040518080602001806020018060200180602001858103855289818151815260200191508051906020019060200280838360005b838110156106b457818101518382015260200161069c565b50505050905001858103845288818151815260200191508051906020019060200280838360005b838110156106f35781810151838201526020016106db565b50505050905001858103835287818151815260200191508051906020019060200280838360005b8381101561073257818101518382015260200161071a565b50505050905001858103825286818151815260200191508051906020019060200280838360005b83811015610771578181015183820152602001610759565b505050509050019850505050505050505060405180910390f35b61021c600480360360608110156107a157600080fd5b508035600290810b91602081013590910b90604001356001600160801b0316612117565b610870600480360360c08110156107db57600080fd5b6001600160a01b0382358116926020810135909116916040820135600290810b92606081013590910b916001600160801b03608083013516919081019060c0810160a0820135600160201b81111561083257600080fd5b82018360208201111561084457600080fd5b803590602001918460018302840111600160201b8311171561086557600080fd5b509092509050612353565b6040805193845260208401929092526001600160801b031682820152519081900360600190f35b61016c612925565b61041e600480360360208110156108b557600080fd5b503560010b612949565b6108c761295b565b6040805160029290920b8252519081900360200190f35b61016c61296b565b6108ee61298f565b604080516001600160a01b03909816885260029690960b602088015261ffff9485168787015292909316606086015260ff90811660808601529190911660a0840152151560c0830152519081900360e00190f35b61041e6129e2565b61029d6004803603602081101561096057600080fd5b503560020b6129e8565b61098a6004803603602081101561098057600080fd5b503560020b612bc9565b604080516001600160801b039099168952600f9790970b602089015287870195909552606087019390935260069190910b60808601526001600160a01b031660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b61029d60048036036020811015610a0257600080fd5b50356001600160a01b0316612c33565b61016c612ea1565b7f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291381565b600080600080600080610a528b8b8b612eb7565b949a509298509096509450925090508a15610b07576000851215610a9e57610a9e7f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca8d87600003613797565b6000610aa86138e5565b9050610ab687878b8b613985565b610abe6138e5565b610ac88289613a25565b1115610b01576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b50610ba3565b6000861215610b3e57610b3e7f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029138d88600003613797565b6000610b48613a3b565b9050610b5687878b8b613985565b610b5e613a3b565b610b688288613a25565b1115610ba1576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b8015610bff57610bff8b610bd7577f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca610bf9565b7f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029135b82613aaa565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e169133916000805160206157f6833981519152919081900360a00190a350506002805460ff60e81b1916600160e81b17905550919890975095505050505050565b60045463ffffffff1681565b6003546001600160801b031681565b7f000000000000000000000000c207628e5e2b59e9c690071e68c7c1c4193b02526001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610cee57600080fd5b505afa158015610d02573d6000803e3d6000fd5b505050506040513d6020811015610d1857600080fd5b50516001600160a01b03163314610d2e57600080fd5b6201518063ffffffff821611801590610d52575060045463ffffffff828116911614155b610d5b57600080fd5b6004805463ffffffff831663ffffffff19909116811790915560408051918252517fb5e51602371b0e74f991b6e965cd7d32b4b14c7e6ede6d1298037650a0e1405f9181900360200190a150565b7f0000000000000000000000009aada071dd48b868213936f2c1442aebcbeb768381565b600254600160e81b900460ff16610e11576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b191690556003546001600160801b031680610e60576040805162461bcd60e51b81526020600482015260016024820152601360fa1b604482015290519081900360640190fd5b600254600160b81b900461ffff16600080610e796138e5565b90508715610ec057610e93888461ffff16620f4240613b43565b9150610ec07f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029138a8a613797565b600080610ecb613a3b565b90508815610f1257610ee5898661ffff16620f4240613b43565b9150610f127f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca8c8b613797565b336001600160a01b031663a60b0d3c85848b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015610f9457600080fd5b505af1158015610fa8573d6000803e3d6000fd5b5050505060007f000000000000000000000000c207628e5e2b59e9c690071e68c7c1c4193b02526001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561100757600080fd5b505afa15801561101b573d6000803e3d6000fd5b505050506040513d602081101561103157600080fd5b50519050600061103f6138e5565b90508061104c8688613a25565b1115611084576040805162461bcd60e51b8152602060048201526002602482015261046360f41b604482015290519081900360640190fd5b84900380156110fe57600254600160d81b900460ff16600081156110d857506103e860ff82168302046110d87f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029138583613797565b6110f2818403600160801b8c6001600160801b0316613bb8565b60008054909101905550505b6000611108613a3b565b9050806111158587613a25565b111561114d576040805162461bcd60e51b8152602060048201526002602482015261463160f01b604482015290519081900360640190fd5b83900380156111c757600254600160e01b900460ff16600081156111a157506103e860ff82168302046111a17f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca8683613797565b6111bb818403600160801b8d6001600160801b0316613bb8565b60018054909101905550505b8d6001600160a01b0316336001600160a01b03167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a350506002805460ff60e81b1916600160e81b179055505050505050505050505050565b6002546000908190600160e81b900460ff16611296576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560006112b0338888613c4e565b60038101549091506001600160801b0380821691600160801b9004811690871682106112dc57866112de565b815b9450806001600160801b0316866001600160801b0316116112ff5785611301565b805b93506001600160801b0385851716156113c5576003830180546001600160801b0319168684036001600160801b03908116919091178116600160801b87850383160217909155851615611382576113827f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029138b876001600160801b0316613797565b6001600160801b038416156113c5576113c57f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca8b866001600160801b0316613797565b604080516001600160a01b038c1681526001600160801b038088166020830152861681830152905160028a810b92908c900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a450506002805460ff60e81b1916600160e81b1790555090969095509350505050565b60076020526000908152604090208054600182015460028301546003909301546001600160801b038084169463ffffffff600160801b9586900416949092808316929190041686565b60005481565b6d09745258e83de0d0f4e400fce79990565b6002546000908190600160e81b900460ff166114f1576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b19169055861561157257600061150e6138e5565b905061151d8760008787613985565b60006115318261152b6138e5565b90613c72565b9750871361156c576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b506115d6565b600061157c613a3b565b905061158b6000888787613985565b60006115998261152b613a3b565b975087136115d4576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b6002805460ff60e81b1916600160e81b17905560008080806115f98b8b8b612eb7565b949a509298509096509450925090508a15611686576000851215611645576116457f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca8d87600003613797565b89861215611681576116817f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029138e61167c8d8a613c82565b613797565b6116f4565b60008612156116bd576116bd7f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029138d88600003613797565b898512156116f4576116f47f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca8e61167c8d89613c82565b8015611728576117288b610bd7577f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca610bf9565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e169133916000805160206157f6833981519152919081900360a00190a350506002805460ff60e81b1916600160e81b1790555091999098509650505050505050565b60008060008060008060007f0000000000000000000000009aada071dd48b868213936f2c1442aebcbeb76836001600160a01b03166374eceae6896040518263ffffffff1660e01b81526004018082815260200191505060e06040518083038186803b15801561181357600080fd5b505afa158015611827573d6000803e3d6000fd5b505050506040513d60e081101561183d57600080fd5b508051602082015160408301516060840151608085015160a086015160c090960151949e939d50919b50995097509195509350915050565b600254600160e81b900460ff166118b9576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560408051638da5cb5b60e01b815290516001600160a01b037f000000000000000000000000c207628e5e2b59e9c690071e68c7c1c4193b02521691638da5cb5b916004808301926020929190829003018186803b15801561192657600080fd5b505afa15801561193a573d6000803e3d6000fd5b505050506040513d602081101561195057600080fd5b50516001600160a01b0316331461196657600080fd5b60fa60ff83161180159061197e575060fa60ff821611155b61198757600080fd5b6002805460ff60e01b1916600160e01b60ff8481169182029290921760ff60d81b1916600160d81b9286169283021790925560408051918252602082019290925281517f9e22b964b08e25c3aaa72102bb0071c089258fb82d51271a8ddf5c24921356ee929181900390910190a150506002805460ff60e81b1916600160e81b179055565b7f000000000000000000000000c207628e5e2b59e9c690071e68c7c1c4193b02526001600160a01b0316638a2ade586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a6557600080fd5b505afa158015611a79573d6000803e3d6000fd5b505050506040513d6020811015611a8f57600080fd5b50516001600160a01b03163314611aa557600080fd5b60048054600160201b600160c01b031916600160201b6001600160a01b038416908102919091179091556040517f915c5369e6580733735d1c2e30ca20dcaa395697a041033c9f35f80f53525e8490600090a250565b600080808484620d89e9600282900b12611b42576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13611b82576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13611bc4576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b611bcc615726565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c3457600080fd5b50611c3d615726565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611ca557600080fd5b5060028054600160a01b8104820b91600160c81b90910461ffff16908b810b9083900b1215611cfe5782600001518460000151038360200151856020015103846040015186604001510398509850985050505050611da9565b8960020b8260020b1215611d7d576000611d16613c98565b9050600080611d3e8360008787600360009054906101000a90046001600160801b0316613c9c565b5050915091508560000151876000015183030386602001518860200151830303876040015189604001518603039b509b509b5050505050505050611da9565b836000015183600001510384602001518460200151038560400151856040015103985098509850505050505b50509250925092565b6060806060807f0000000000000000000000009aada071dd48b868213936f2c1442aebcbeb76836001600160a01b031663fd31e988611def613c98565b6002805460035460405160e086901b6001600160e01b031916815263ffffffff851660048201908152600160a01b8404850b9485900b6044830152600160c81b90930461ffff16606482018190526001600160801b039092166084820181905260a06024830190815260a483018e90528e958e9590949390919060c401876020880280828437600081840152601f19601f82011690508083019250505097505050505050505060006040518083038186803b158015611ead57600080fd5b505afa158015611ec1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526080811015611eea57600080fd5b8101908080516040519392919084600160201b821115611f0957600080fd5b908301906020820185811115611f1e57600080fd5b82518660208202830111600160201b82111715611f3a57600080fd5b82525081516020918201928201910280838360005b83811015611f67578181015183820152602001611f4f565b5050505090500160405260200180516040519392919084600160201b821115611f8f57600080fd5b908301906020820185811115611fa457600080fd5b82518660208202830111600160201b82111715611fc057600080fd5b82525081516020918201928201910280838360005b83811015611fed578181015183820152602001611fd5565b5050505090500160405260200180516040519392919084600160201b82111561201557600080fd5b90830190602082018581111561202a57600080fd5b82518660208202830111600160201b8211171561204657600080fd5b82525081516020918201928201910280838360005b8381101561207357818101518382015260200161205b565b5050505090500160405260200180516040519392919084600160201b82111561209b57600080fd5b9083019060208201858111156120b057600080fd5b82518660208202830111600160201b821117156120cc57600080fd5b82525081516020918201928201910280838360005b838110156120f95781810151838201526020016120e1565b50505050905001604052505050935093509350935092959194509250565b6002546000908190600160e81b900460ff16612160576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916815585908590620d89e99082900b126121b1576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b136121f1576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612233576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b6000806000612258338b8b6122508c6001600160801b0316613d8c565b600003613da2565b9250925092508160000396508060000395508587176000146122d957600383015461228c906001600160801b031688614078565b60038401546122ab90600160801b90046001600160801b031688614078565b6003850180546001600160801b03938416928416600160801b029316929092176001600160801b0319161790555b604080516001600160801b038a16815260208101899052808201889052905160028b810b92908d900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a450506002805460ff60e81b1916600160e81b179055509296919550909350505050565b60025460009081908190600160e81b900460ff1661239e576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916815588908890620d89e99082900b126123ef576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b1361242f576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612471576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b6000886001600160801b0316116124b4576040805162461bcd60e51b8152602060048201526002602482015261125360f21b604482015290519081900360640190fd5b600454600160c01b9004600290810b9081810b908b900b816124d257fe5b078160020b8c60020b816124e257fe5b071760020b1561252e576040805162461bcd60e51b81526020600482015260126024820152711d1a58dac81a5cc81b9bdd081cdc1858d95960721b604482015290519081900360640190fd5b506000806125668c8c6125498d6001600160801b0316613d8c565b60028054600160a01b810490910b906001600160a01b0316614094565b509097509550600091508190508615612584576125816138e5565b91505b851561259557612592613a3b565b90505b336001600160a01b0316633dd657c588888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561261757600080fd5b505af115801561262b573d6000803e3d6000fd5b505050506000871115612682576000826126436138e5565b0392508211612682576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b85156126d257600081612693613a3b565b03915081116126d2576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b899450868210156126f4576126f18a6001600160801b03168389613bb8565b94505b858110156127345760006127128b6001600160801b03168389613bb8565b9050856001600160801b0316816001600160801b03161015612732578095505b505b6000856001600160801b03161161277b576040805162461bcd60e51b8152602060048083019190915260248201526324a4a61960e11b604482015290519081900360640190fd5b60008061279b8f8f8f6127968b6001600160801b0316613d8c565b613da2565b9250925050838299508911156127e0576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b82819850881115612820576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b505086821115612857576128577f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029138f898503613797565b8581111561288c5761288c7f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca8f888403613797565b8a60020b8c60020b8e6001600160a01b03167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde33898c8c60405180856001600160a01b03168152602001846001600160801b0316815260200183815260200182815260200194505050505060405180910390a450506002805460ff60e81b1916600160e81b17905550929a919950975095505050505050565b7f000000000000000000000000c207628e5e2b59e9c690071e68c7c1c4193b025281565b60066020526000908152604090205481565b600454600160c01b900460020b81565b7f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca81565b600280546001600160a01b03811691600160a01b8204900b9061ffff600160b81b8204811691600160c81b81049091169060ff600160d81b8204811691600160e01b8104821691600160e81b9091041687565b60015481565b600254600160e81b900460ff16612a2c576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560408051638da5cb5b60e01b815290516001600160a01b037f000000000000000000000000c207628e5e2b59e9c690071e68c7c1c4193b02521691638da5cb5b916004808301926020929190829003018186803b158015612a9957600080fd5b505afa158015612aad573d6000803e3d6000fd5b505050506040513d6020811015612ac357600080fd5b50516001600160a01b03163314612ad957600080fd5b60008160020b138015612af257506101f4600282900b13155b8015612b105750600454600282810b600160c01b909204810b900b14155b612b5a576040805162461bcd60e51b8152602060048201526016602482015275496e76616c6964206e65775469636b53706163696e6760501b604482015290519081900360640190fd5b60048054600283900b62ffffff8116600160c01b0262ffffff60c01b199092169190911790915560408051918252517f01413b1d5d4c359e9a0daa7909ecda165f6e8c51fe2ff529d74b22a5a7c026459181900360200190a1506002805460ff60e81b1916600160e81b179055565b60056020526000908152604090208054600182015460028301546003909301546001600160801b03831693600160801b909304600f0b9290600681900b90600160381b81046001600160a01b031690600160d81b810463ffffffff1690600160f81b900460ff1688565b6002546001600160a01b031615612c76576040805162461bcd60e51b8152602060048201526002602482015261414960f01b604482015290519081900360640190fd5b6000612c818261412c565b905060007f000000000000000000000000c207628e5e2b59e9c690071e68c7c1c4193b02526001600160a01b0316632f8a39dd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612cde57600080fd5b505afa158015612cf2573d6000803e3d6000fd5b505050506040513d6020811015612d0857600080fd5b505190506000612d16613c98565b90507f0000000000000000000000009aada071dd48b868213936f2c1442aebcbeb76836001600160a01b031663475fb80c82856040518363ffffffff1660e01b8152600401808363ffffffff1681526020018260020b815260200192505050600060405180830381600087803b158015612d8f57600080fd5b505af1158015612da3573d6000803e3d6000fd5b5050505083600260000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550816002600001601b6101000a81548160ff021916908360ff160217905550816002600001601c6101000a81548160ff021916908360ff16021790555060016002600001601d6101000a81548160ff02191690831515021790555082600260000160146101000a81548162ffffff021916908360020b62ffffff1602179055507f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95848460405180836001600160a01b031681526020018260020b81526020019250505060405180910390a150505050565b600454600160201b90046001600160a01b031681565b6000806000806000806000612eca615746565b6002805461ffff600160b81b82048116610160850152600160c81b8204166101a084015260ff60e81b19811682556001600160a01b0381169750600160a01b810490910b955060ff600160d81b8204811691600160e01b8104821691600160e81b9091041680612f67576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b8c612f9e576040805162461bcd60e51b8152602060048201526002602482015261415360f01b604482015290519081900360640190fd5b60008d1361014085015260a084018d90526003546001600160801b03600160801b8204811660208701521696508d1561304657886001600160a01b03168c6001600160a01b0316108015612fff57506401000276a36001600160a01b038d16115b613036576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60005460e08501528284526130c6565b886001600160a01b03168c6001600160a01b0316118015613083575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038d16105b6130ba576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60015460e08501528184525b600288810b900b6101808501526130db613c98565b600454909550600160201b90046001600160a01b0316156131f157600060048054906101000a90046001600160a01b03166001600160a01b0316637f376059876040518263ffffffff1660e01b8152600401808263ffffffff168152602001915050602060405180830381600087803b15801561315757600080fd5b505af115801561316b573d6000803e3d6000fd5b505050506040513d602081101561318157600080fd5b50519050600081600281111561319357fe5b14156131b15760048054600160201b600160c01b03191690556131ef565b60018160028111156131bf57fe5b14156131d25760016101208601526131ef565b60028160028111156131e057fe5b14156131ef5760026101208601525b505b600061320e856101a00151878761018001518b8960200151614417565b9050846101a0015161ffff168161ffff161461324d5761ffff81166101a086015260006020860152613242868a838b6144eb565b61ffff166101608601525b505050506132596157b7565b6001600160a01b03871681526132716006878e6145ed565b15156040830152600290810b900b6020820181905261328f906146d6565b6001600160a01b03908116606083018190526132cd918e918a918e1611821515146132be5783606001516132c0565b8c5b888f8761016001516149f0565b60c085015260a0840152608083015261014083015190975015613326576132fd8160c00151826080015101614bb4565b8b039a5061331c6133118260a00151614bb4565b60c084015190613c82565b60c083015261335e565b6133338160a00151614bb4565b8b019a5061335861334d8260c00151836080015101614bb4565b60c084015190614bca565b60c08301525b81511561339a57815160c08201516000916103e89161337c91614be0565b8161338357fe5b60c084018051929091049182900390529490940193505b6001600160801b038516156133d1576133c58160c00151600160801b876001600160801b0316613bb8565b60e08301805190910190525b80606001516001600160a01b0316876001600160a01b03161415613598578060400151156135795781608001516134555761341a836000846101800151856101a0015189613c9c565b50506001600160a01b03166060840152600690810b900b6040830152600160808301528b61344a5760005461344e565b6001545b6101008301525b6000826101200151600281111561346857fe5b146134f05760048054906101000a90046001600160a01b03166001600160a01b03166301342b1982602001518e6040518363ffffffff1660e01b8152600401808360020b8152602001821515815260200192505050600060405180830381600087803b1580156134d757600080fd5b505af11580156134eb573d6000803e3d6000fd5b505050505b60008c156135355761352b82602001518460e0015185610100015186606001518760400151896005614c04909695949392919063ffffffff16565b600003905061356b565b61356882602001518461010001518560e0015186606001518760400151896005614c04909695949392919063ffffffff16565b90505b6135758682614cb8565b9550505b8b613588578060200151613591565b60018160200151035b95506135bc565b80516001600160a01b038881169116146135bc576135b58761412c565b95506135e9565b8a15806135da5750896001600160a01b0316876001600160a01b0316145b156135e4576135e9565b613259565b81610140015115158c15151461360a578160c001518b8360a0015103613617565b8a8260a00151038260c001515b6101608401516101a08501516002805461ffff60c81b1916600160c81b61ffff938416021761ffff60b81b1916600160b81b92909316919091029190911762ffffff60a01b1916600160a01b62ffffff8b840b1602176001600160a01b0319166001600160a01b038b81169190911790915560408051631b7297f760e11b81526001600160801b038a16600482015260248101859052604481018490529051939c50919a5087927f0000000000000000000000009aada071dd48b868213936f2c1442aebcbeb7683909116916336e52fee916064808301926020929190829003018186803b15801561370857600080fd5b505afa15801561371c573d6000803e3d6000fd5b505050506040513d602081101561373257600080fd5b50516020840151600380546001600160801b03948516929093018416600160801b0293909216929092176001600160801b0319169190911790558b1561377f5760e0820151600055613788565b60e08201516001555b50505093975093979195509350565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b602083106138135780518252601f1990920191602091820191016137f4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613875576040519150601f19603f3d011682016040523d82523d6000602084013e61387a565b606091505b50915091508180156138a85750805115806138a857508080602001905160208110156138a557600080fd5b50515b6138de576040805162461bcd60e51b81526020600482015260026024820152612a2360f11b604482015290519081900360640190fd5b5050505050565b60007f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029136001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561395457600080fd5b505afa158015613968573d6000803e3d6000fd5b505050506040513d602081101561397e57600080fd5b5051905090565b336001600160a01b0316632c8958f6858585856040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015613a0757600080fd5b505af1158015613a1b573d6000803e3d6000fd5b5050505050505050565b80820182811015613a3557600080fd5b92915050565b60007f000000000000000000000000d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561395457600080fd5b60007f000000000000000000000000c207628e5e2b59e9c690071e68c7c1c4193b02526001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613b0557600080fd5b505afa158015613b19573d6000803e3d6000fd5b505050506040513d6020811015613b2f57600080fd5b50519050613b3e838284613797565b505050565b6000831580613b5e57505082820282848281613b5b57fe5b04145b15613b7f5760008211613b7057600080fd5b81810490829006151501613bb1565b613b8a848484613bb8565b905060008280613b9657fe5b8486091115613bb1576000198110613bad57600080fd5b6001015b9392505050565b60008383028160001985870982811083820303915050808411613bda57600080fd5b80613bea57508290049050613bb1565b8385870960008581038616958690049560026003880281188089028203028089028203028089028203028089028203028089028203028089029091030291819003819004600101858411909403939093029190930391909104170290509392505050565b62ffffff9081169116601892831b1790911b17600090815260076020526040902090565b80820382811115613a3557600080fd5b80820382811315600083121514613a3557600080fd5b4290565b604080516314c5407960e01b815263ffffffff808816600483015286166024820152600285900b604482015261ffff841660648201526001600160801b038316608482015290516000918291829182916001600160a01b037f0000000000000000000000009aada071dd48b868213936f2c1442aebcbeb768316916314c540799160a4808301926080929190829003018186803b158015613d3c57600080fd5b505afa158015613d50573d6000803e3d6000fd5b505050506040513d6080811015613d6657600080fd5b50805160208201516040830151606090930151919c909b50919950975095505050505050565b80600f81900b8114613d9d57600080fd5b919050565b60408051606081018252600280546001600160a01b0381168352600160a01b8104820b90910b6020830152600160c81b900461ffff169181019190915260009081908190613df1888888613c4e565b60008054600154929650919080600f89900b15613eb8576000613e12613c98565b9050600080613e428360008a602001518b60400151600360009054906101000a90046001600160801b0316613c9c565b505091509150613e718e89602001518e8a8a86888a60006005614d6e909998979695949392919063ffffffff16565b15613e855760019450613e8560068f614f08565b6020880151613ea0906005908f908f8b8b87898b6001614d6e565b15613eb45760019350613eb460068e614f08565b5050505b60208501516000908190613ed4906005908f908f908a8a614f34565b91509150613ee48a8c8484614fd6565b8a600f0b6000146140675760008b600f0b1215613f5f578315613f2d5760028d810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b8215613f5f5760028c810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b6000613f768e8e8e8b602001518c60000151614094565b919b5099509050600f81900b156140655760035460408901516001600160801b0390911690600090613fc990613faa613c98565b60208d01516003548690600160801b90046001600160801b0316614417565b90508061ffff168a6040015161ffff161461403857613ff5613fe9613c98565b8b6020015183856144eb565b6002805461ffff60b81b1916600160b81b61ffff938416021761ffff60c81b1916600160c81b92841692909202919091179055600380546001600160801b031690555b614042828f614cb8565b600380546001600160801b0319166001600160801b039290921691909117905550505b505b505050505050509450945094915050565b8082016001600160801b038084169082161015613a3557600080fd5b60008060008760020b8560020b12156140c9576140c26140b3896146d6565b6140bc896146d6565b886151b4565b9250614121565b8660020b8560020b1215614103576140e4846140bc896146d6565b92506140f96140f2896146d6565b85886151f9565b9150859050614121565b61411e61410f896146d6565b614118896146d6565b886151f9565b91505b955095509592505050565b60006401000276a36001600160a01b03831610801590614168575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038316105b61419d576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b600160201b600160c01b03602083901b166001600160801b03811160071b81811c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c9790881196179094179092171790911717176080811061422f57607f810383901c9150614239565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d607f198f0160401b60c09190911c6001603f1b161760c19b909b1c6001603e1b169a909a1760c29990991c6001603d1b169890981760c39790971c6001603c1b169690961760c49590951c6001603b1b169490941760c59390931c6001603a1b169290921760c69190911c600160391b161760c79190911c600160381b161760c89190911c600160371b161760c99190911c600160361b161760ca9190911c600160351b161760cb9190911c600160341b161760cc9190911c600160331b161760cd9190911c600160321b1617693627a301d71055774c8581026f028f6481ab7f045a5af012a19d003aa9198101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b1461440857886001600160a01b03166143ec826146d6565b6001600160a01b031611156144015781614403565b805b61440a565b815b9998505050505050505050565b60408051630eea437960e11b815261ffff8716600482015263ffffffff86166024820152600285900b60448201526001600160801b0380851660648301528316608482015290516000916001600160a01b037f0000000000000000000000009aada071dd48b868213936f2c1442aebcbeb76831691631dd486f29160a48082019260209290919082900301818787803b1580156144b357600080fd5b505af11580156144c7573d6000803e3d6000fd5b505050506040513d60208110156144dd57600080fd5b505190505b95945050505050565b6040805163c53a182f60e01b815263ffffffff86166004820152600285900b602482015261ffff841660448201526001600160801b038316606482015290516000916001600160a01b037f0000000000000000000000009aada071dd48b868213936f2c1442aebcbeb7683169163c53a182f91608480820192602092909190829003018186803b15801561457e57600080fd5b505afa158015614592573d6000803e3d6000fd5b505050506040513d60208110156145a857600080fd5b50516040805161ffff8316815290519192507f598b9f043c813aa6be3426ca60d1c65d17256312890be5118dab55b0775ebe2a919081900360200190a1949350505050565b600080821561466d57600884901c600181810b900b60009081526020879052604090205460ff868116918282039091161b801561464e5761462d81615229565b60ff0360ff16870396506146408761526c565b6001945094505050506146ce565b8160ff168703965061465f8761526c565b6000945094505050506146ce565b6001938401600881901c80860b90950b60009081526020879052604090205490949060ff861690811c80156146ba576146aa8182600003166152a1565b60ff16870196506146408761526c565b8160ff0360ff168701965061465f8761526c565b935093915050565b6000600282810b60171d90818418829003900b620d89e8811115614725576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b60006001821661473957600160801b61474b565b6ffffcb933bd6fad37aa2d162d1a5940015b6001600160881b031690506002821615614775576ffff97272373d413259a46990580e213a0260801c5b6004821615614794576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b60088216156147b3576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b60108216156147d2576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156147f1576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615614810576fff2ea16466c96a3843ec78b326b528610260801c5b608082161561482f576ffe5dee046a99a2a811c461f1969c30530260801c5b61010082161561484f576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b61020082161561486f576ff987a7253ac413176f2b074cf7815e540260801c5b61040082161561488f576ff3392b0822b70005940c7a398e4b70f30260801c5b6108008216156148af576fe7159475a2c29b7443b29c7fa6e889d90260801c5b6110008216156148cf576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156148ef576fa9f746462d870fdf8a65dc1f90e061e50260801c5b61400082161561490f576f70d869a156d2a1b890bb3df62baf32f70260801c5b61800082161561492f576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615614950576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615614970576e5d6af8dedb81196699c329225ee6040260801c5b6204000082161561498f576d2216e584f5fa1ea926041bedfe980260801c5b620800008216156149ac576b048a170391f7dc42444e8fa20260801c5b60008560020b13156149c75780600019816149c357fe5b0490505b600160201b8106156149da5760016149dd565b60005b60ff16602082901c019350505050919050565b6000806000806157f38a614a06576153b5614a0a565b6153c45b905060008712614af6576000614a32888861ffff16620f42400362ffffff16620f4240613bb8565b9050614a438a8c8b8563ffffffff16565b9450848110614a7057899550614a698561ffff891662ffffff620f424082900316613b43565b9250614acf565b614a7c8b8a838f6153d3565b9550856001600160a01b03168a6001600160a01b031614614ab257614aa6868c8b8563ffffffff16565b94508488039250614acf565b614acc8561ffff891662ffffff620f424082900316613b43565b92505b614aee868c8b8f614ae2576153e3614ae6565b6153f25b63ffffffff16565b935050614ba6565b6157f38b614b06576153e3614b0a565b6153f25b9050614b1b8a8c8b8463ffffffff16565b9350876000039750838810614b3257899550614b77565b614b3e8b8a8a8f615401565b9550856001600160a01b03168a6001600160a01b031614614b6b57614b68868c8b8463ffffffff16565b93505b87841115614b77578793505b614b86868c8b8563ffffffff16565b9450614ba28561ffff891662ffffff620f424082900316613b43565b9250505b509650965096509692505050565b6000600160ff1b8210614bc657600080fd5b5090565b81810182811215600083121514613a3557600080fd5b6000821580614bfb57505081810281838281614bf857fe5b04145b613a3557600080fd5b600286810b810b60009081526020899052604090206003810180546001600160a01b03600160381b63ffffffff600160d81b808504821689039091160263ffffffff60d81b199093169290921782810482168903909116909102600160381b600160d81b031990911617600681810b8703900b66ffffffffffffff1666ffffffffffffff199091161790559081018054860390556001810180548703905554600160801b9004600f0b979650505050505050565b60008082600f0b1215614d1d57826001600160801b03168260000384039150816001600160801b031610614d18576040805162461bcd60e51b81526020600482015260026024820152614c5360f01b604482015290519081900360640190fd5b613a35565b826001600160801b03168284019150816001600160801b03161015613a35576040805162461bcd60e51b81526020600482015260026024820152614c4160f01b604482015290519081900360640190fd5b600289810b900b600090815260208b9052604081208054600160801b8104600f0b906001600160801b031683614da4828d614cb8565b90506d09745258e83de0d0f4e400fce79a6001600160801b03821610614df6576040805162461bcd60e51b81526020600482015260026024820152614c4f60f01b604482015290519081900360640190fd5b85614e1957614e14614e0f600f85810b908f900b614bca565b613d8c565b614e2d565b614e2d614e0f600f85810b908f900b613c82565b84546001600160801b03838116600f9390930b8116600160801b02918116919091176001600160801b03191682178655901595508216614ef657841594508c60020b8e60020b13614ede57600184018b9055600284018a9055600384018054600160381b600160d81b031916600160381b6001600160a01b038c16021766ffffffffffffff191666ffffffffffffff60068b900b161763ffffffff60d81b1916600160d81b63ffffffff8a16021790555b6003840180546001600160f81b0316600160f81b1790555b505050509a9950505050505050505050565b600881901c600190810b810b60009081526020939093526040909220805460ff9092169290921b189055565b600285810b810b60009081526020889052604080822087840b80850b84529183209293849391929088900b1215614fae578860020b8760020b12614f8957816001015486039350816002015485039250614f98565b81600101549350816002015492505b6001810154600282015494039390920391614fc9565b81600101548160010154039350816002015481600201540392505b5050965096945050505050565b83546001600160801b03811690600160801b900463ffffffff16600f85900b615041576000826001600160801b03161161503c576040805162461bcd60e51b815260206004820152600260248201526104e560f41b604482015290519081900360640190fd5b6150fa565b600085600f0b12156150805760045463ffffffff16801561507e578063ffffffff168261506c613c98565b0363ffffffff16101561507e57600080fd5b505b600061508c8387614cb8565b9050806000826001600160801b0316116150a75760006150c0565b600087600f0b136150b857826150c0565b6150c0613c98565b88546001600160801b039092166001600160801b031963ffffffff909216600160801b0263ffffffff60801b199093169290921716178755505b600186015460028701546000868314615131576001890187905561512e8388036001600160801b038716600160801b613bb8565b90505b600082871461515e5760028a0187905561515b8388036001600160801b038816600160801b613bb8565b90505b6001600160801b0382821716156151a85760038a0180546001600160801b031981166001600160801b039182168501821617808216600160801b9182900483168501909216021790555b50505050505050505050565b60008082600f0b12156151e1576151d96151d48585856000036000615411565b614bb4565b6000036151f1565b6151f16151d48585856001615411565b949350505050565b60008082600f0b1215615219576151d96151d485858560000360006154a0565b6151f16151d485858560016154a0565b600181811c909117600281901c17600481901c17600881901c17601081901c17602081901c17604081901c17608081901c179081901c90036000613a35826152a1565b80620d89e719600282900b12156152885750620d89e719613d9d565b620d89e8600282900b1315613d9d5750620d89e8919050565b7f55555555555555555555555555555555555555555555555555555555555555558116156001600160801b0382161560071b176001600160401b03600160801b03600160c01b0382161560061b177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff82161560051b177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff82161560041b177eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff82161560031b177f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f82161560021b177f3333333333333333333333333333333333333333333333333333333333333333919091161560011b1790565b60006151f183858460016154a0565b60006151f18484846001615411565b60006144e2858585856001615511565b60006151f18385846000615411565b60006151f184848460006154a0565b60006144e2858585856000615511565b60006001600160a01b038585038116908516811061542e57600080fd5b600160601b600160e01b03606085901b168361546f57866001600160a01b03166154628383896001600160a01b0316613bb8565b8161546957fe5b04615495565b6154956154868383896001600160a01b0316613b43565b886001600160a01b0316615705565b979650505050505050565b6000846001600160a01b0316846001600160a01b031610156154c157600080fd5b6001600160a01b0385850316826154ef576154ea81856001600160801b0316600160601b613bb8565b615507565b61550781856001600160801b0316600160601b613b43565b9695505050505050565b600080866001600160a01b03161161552857600080fd5b6000856001600160801b03161161553e57600080fd5b811515831515141561563057836155565750846144e2565b600160601b600160e01b03606086901b1682156155e4576001600160a01b0387168581029086828161558457fe5b0414156155b5578181018281106155b3576155a9838a6001600160a01b031683613b43565b93505050506144e2565b505b6155db826155d6888b6001600160a01b031686816155cf57fe5b0490613a25565b615705565b925050506144e2565b6001600160a01b038716858102908682816155fb57fe5b041461560657600080fd5b80821161561257600080fd5b6155db61562b838a6001600160a01b0316848603613b43565b615710565b81156156975761569061562b6001600160a01b038611156156685761566386600160601b896001600160801b0316613bb8565b615680565b6001600160801b038716606087901b8161567e57fe5b045b6001600160a01b03891690613a25565b90506144e2565b60006001600160a01b038511156156c5576156c085600160601b886001600160801b0316613b43565b6156dc565b6156dc606086901b6001600160801b038816615705565b905080876001600160a01b0316116156f357600080fd5b6001600160a01b0387160390506144e2565b808204910615150190565b806001600160a01b0381168114613d9d57600080fd5b604080516060810182526000808252602082018190529181019190915290565b604080516101c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052906101208201908152600060208201819052604082018190526060820181905260809091015290565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565bfefec42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a164736f6c6343000706000a
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.