More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Set Community Fe... | 13160023 | 122 days ago | IN | 0 ETH | 0.00000529 |
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
10899206 | 201 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xFC4A3A7d...006083b83 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
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'; 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; } 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); uint32 timestamp = _blockTimestamp(); IDataStorageOperator(dataStorageOperator).initialize(timestamp, tick); globalState.price = initialPrice; 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'); { (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(liquidityActual), receivedAmount0, amount0)); } if (receivedAmount1 < amount1) { uint128 liquidityForRA1 = uint128(FullMath.mulDiv(uint256(liquidityActual), 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 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; pragma abicoder v2; import './interfaces/IAlgebraFactory.sol'; import './interfaces/IAlgebraPoolDeployer.sol'; import './interfaces/IDataStorageOperator.sol'; import './libraries/AdaptiveFee.sol'; import './DataStorageOperator.sol'; /** * @title Algebra factory * @notice Is used to deploy pools and its dataStorages */ contract AlgebraFactory is IAlgebraFactory { /// @inheritdoc IAlgebraFactory address public override owner; /// @inheritdoc IAlgebraFactory address public immutable override poolDeployer; /// @inheritdoc IAlgebraFactory address public override farmingAddress; /// @inheritdoc IAlgebraFactory address public override vaultAddress; // values of constants for sigmoids in fee calculation formula AdaptiveFee.Configuration public baseFeeConfiguration = AdaptiveFee.Configuration( 3000 - Constants.BASE_FEE, // alpha1 15000 - 3000, // alpha2 360, // beta1 60000, // beta2 59, // gamma1 8500, // gamma2 0, // volumeBeta 10, // volumeGamma Constants.BASE_FEE // baseFee ); modifier onlyOwner() { require(msg.sender == owner); _; } /// @inheritdoc IAlgebraFactory mapping(address => mapping(address => address)) public override poolByPair; constructor(address _poolDeployer, address _vaultAddress) { owner = msg.sender; emit Owner(msg.sender); poolDeployer = _poolDeployer; vaultAddress = _vaultAddress; } /// @inheritdoc IAlgebraFactory function createPool(address tokenA, address tokenB) external override returns (address pool) { require(tokenA != tokenB); (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != address(0)); require(poolByPair[token0][token1] == address(0)); IDataStorageOperator dataStorage = new DataStorageOperator(computeAddress(token0, token1)); dataStorage.changeFeeConfiguration(baseFeeConfiguration); pool = IAlgebraPoolDeployer(poolDeployer).deploy(address(dataStorage), address(this), token0, token1); poolByPair[token0][token1] = pool; // to avoid future addresses comparing we are populating the mapping twice poolByPair[token1][token0] = pool; emit Pool(token0, token1, pool); } /// @inheritdoc IAlgebraFactory function setOwner(address _owner) external override onlyOwner { require(owner != _owner); emit Owner(_owner); owner = _owner; } /// @inheritdoc IAlgebraFactory function setFarmingAddress(address _farmingAddress) external override onlyOwner { require(farmingAddress != _farmingAddress); emit FarmingAddress(_farmingAddress); farmingAddress = _farmingAddress; } /// @inheritdoc IAlgebraFactory function setVaultAddress(address _vaultAddress) external override onlyOwner { require(vaultAddress != _vaultAddress); emit VaultAddress(_vaultAddress); vaultAddress = _vaultAddress; } /// @inheritdoc IAlgebraFactory function setBaseFeeConfiguration( uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint32 volumeBeta, uint16 volumeGamma, uint16 baseFee ) external override onlyOwner { require(uint256(alpha1) + uint256(alpha2) + uint256(baseFee) <= type(uint16).max, 'Max fee exceeded'); require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0'); baseFeeConfiguration = AdaptiveFee.Configuration(alpha1, alpha2, beta1, beta2, gamma1, gamma2, volumeBeta, volumeGamma, baseFee); emit FeeConfiguration(alpha1, alpha2, beta1, beta2, gamma1, gamma2, volumeBeta, volumeGamma, baseFee); } bytes32 internal constant POOL_INIT_CODE_HASH = 0x6ec6c9c8091d160c0aa74b2b14ba9c1717e95093bd3ac085cee99a49aab294a4; /// @notice Deterministically computes the pool address given the factory and PoolKey /// @param token0 first token /// @param token1 second token /// @return pool The contract address of the Algebra pool function computeAddress(address token0, address token1) internal view returns (address pool) { pool = address(uint256(keccak256(abi.encodePacked(hex'ff', poolDeployer, keccak256(abi.encode(token0, token1)), POOL_INIT_CODE_HASH)))); } }
// 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); 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); 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 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; /** * @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; 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: observe(). 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: 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: BUSL-1.1 pragma solidity =0.7.6; pragma abicoder v2; import './interfaces/IAlgebraFactory.sol'; import './interfaces/IDataStorageOperator.sol'; import './libraries/DataStorage.sol'; import './libraries/Sqrt.sol'; import './libraries/AdaptiveFee.sol'; import './libraries/Constants.sol'; contract DataStorageOperator is IDataStorageOperator { uint256 constant UINT16_MODULO = 65536; uint128 constant MAX_VOLUME_PER_LIQUIDITY = 100000 << 64; // maximum meaningful ratio of volume to liquidity using DataStorage for DataStorage.Timepoint[UINT16_MODULO]; DataStorage.Timepoint[UINT16_MODULO] public override timepoints; AdaptiveFee.Configuration public feeConfig; address private immutable pool; address private immutable factory; modifier onlyPool() { require(msg.sender == pool, 'only pool can call this'); _; } constructor(address _pool) { factory = msg.sender; pool = _pool; } /// @inheritdoc IDataStorageOperator function initialize(uint32 time, int24 tick) external override onlyPool { return timepoints.initialize(time, tick); } /// @inheritdoc IDataStorageOperator function changeFeeConfiguration(AdaptiveFee.Configuration calldata _feeConfig) external override { require(msg.sender == factory || msg.sender == IAlgebraFactory(factory).owner()); require(uint256(_feeConfig.alpha1) + uint256(_feeConfig.alpha2) + uint256(_feeConfig.baseFee) <= type(uint16).max, 'Max fee exceeded'); require(_feeConfig.gamma1 != 0 && _feeConfig.gamma2 != 0 && _feeConfig.volumeGamma != 0, 'Gammas must be > 0'); feeConfig = _feeConfig; emit FeeConfiguration(_feeConfig); } /// @inheritdoc IDataStorageOperator function getSingleTimepoint( uint32 time, uint32 secondsAgo, int24 tick, uint16 index, uint128 liquidity ) external view override onlyPool returns ( int56 tickCumulative, uint160 secondsPerLiquidityCumulative, uint112 volatilityCumulative, uint256 volumePerAvgLiquidity ) { uint16 oldestIndex; // check if we have overflow in the past uint16 nextIndex = index + 1; // considering overflow if (timepoints[nextIndex].initialized) { oldestIndex = nextIndex; } DataStorage.Timepoint memory result = timepoints.getSingleTimepoint(time, secondsAgo, tick, index, oldestIndex, liquidity); (tickCumulative, secondsPerLiquidityCumulative, volatilityCumulative, volumePerAvgLiquidity) = ( result.tickCumulative, result.secondsPerLiquidityCumulative, result.volatilityCumulative, result.volumePerLiquidityCumulative ); } /// @inheritdoc IDataStorageOperator function getTimepoints( uint32 time, uint32[] memory secondsAgos, int24 tick, uint16 index, uint128 liquidity ) external view override onlyPool returns ( int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, uint112[] memory volatilityCumulatives, uint256[] memory volumePerAvgLiquiditys ) { return timepoints.getTimepoints(time, secondsAgos, tick, index, liquidity); } /// @inheritdoc IDataStorageOperator function getAverages( uint32 time, int24 tick, uint16 index, uint128 liquidity ) external view override onlyPool returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage) { return timepoints.getAverages(time, tick, index, liquidity); } /// @inheritdoc IDataStorageOperator function write( uint16 index, uint32 blockTimestamp, int24 tick, uint128 liquidity, uint128 volumePerLiquidity ) external override onlyPool returns (uint16 indexUpdated) { return timepoints.write(index, blockTimestamp, tick, liquidity, volumePerLiquidity); } /// @inheritdoc IDataStorageOperator function calculateVolumePerLiquidity( uint128 liquidity, int256 amount0, int256 amount1 ) external pure override returns (uint128 volumePerLiquidity) { uint256 volume = Sqrt.sqrtAbs(amount0) * Sqrt.sqrtAbs(amount1); uint256 volumeShifted; if (volume >= 2**192) volumeShifted = (type(uint256).max) / (liquidity > 0 ? liquidity : 1); else volumeShifted = (volume << 64) / (liquidity > 0 ? liquidity : 1); if (volumeShifted >= MAX_VOLUME_PER_LIQUIDITY) return MAX_VOLUME_PER_LIQUIDITY; else return uint128(volumeShifted); } /// @inheritdoc IDataStorageOperator function window() external pure override returns (uint32) { return DataStorage.WINDOW; } /// @inheritdoc IDataStorageOperator function getFee( uint32 _time, int24 _tick, uint16 _index, uint128 _liquidity ) external view override onlyPool returns (uint16 fee) { (uint88 volatilityAverage, uint256 volumePerLiqAverage) = timepoints.getAverages(_time, _tick, _index, _liquidity); return AdaptiveFee.getFee(volatilityAverage / 15, volumePerLiqAverage, feeConfig); } }
// 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 TICK_SPACING = 60; // max(uint128) / ( (MAX_TICK - MIN_TICK) / TICK_SPACING ) uint128 internal constant MAX_LIQUIDITY_PER_TICK = 11505743598341114571880798222544994; 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: BUSL-1.1 pragma solidity =0.7.6; import './FullMath.sol'; /// @title DataStorage /// @notice Provides price, liquidity, volatility data useful for a wide variety of system designs /// @dev Instances of stored dataStorage data, "timepoints", are collected in the dataStorage array /// Timepoints are overwritten when the full length of the dataStorage array is populated. /// The most recent timepoint is available by passing 0 to getSingleTimepoint() library DataStorage { uint32 public constant WINDOW = 1 days; uint256 private constant UINT16_MODULO = 65536; struct Timepoint { bool initialized; // whether or not the timepoint is initialized uint32 blockTimestamp; // the block timestamp of the timepoint int56 tickCumulative; // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized uint160 secondsPerLiquidityCumulative; // the seconds per liquidity since the pool was first initialized uint88 volatilityCumulative; // the volatility accumulator; overflow after ~34800 years is desired :) int24 averageTick; // average tick at this blockTimestamp uint144 volumePerLiquidityCumulative; // the gmean(volumes)/liquidity accumulator } /// @notice Calculates volatility between two sequential timepoints with resampling to 1 sec frequency /// @param dt Timedelta between timepoints, must be within uint32 range /// @param tick0 The tick at the left timepoint, must be within int24 range /// @param tick1 The tick at the right timepoint, must be within int24 range /// @param avgTick0 The average tick at the left timepoint, must be within int24 range /// @param avgTick1 The average tick at the right timepoint, must be within int24 range /// @return volatility The volatility between two sequential timepoints /// If the requirements for the parameters are met, it always fits 88 bits function _volatilityOnRange( int256 dt, int256 tick0, int256 tick1, int256 avgTick0, int256 avgTick1 ) internal pure returns (uint256 volatility) { // On the time interval from the previous timepoint to the current // we can represent tick and average tick change as two straight lines: // tick = k*t + b, where k and b are some constants // avgTick = p*t + q, where p and q are some constants // we want to get sum of (tick(t) - avgTick(t))^2 for every t in the interval (0; dt] // so: (tick(t) - avgTick(t))^2 = ((k*t + b) - (p*t + q))^2 = (k-p)^2 * t^2 + 2(k-p)(b-q)t + (b-q)^2 // since everything except t is a constant, we need to use progressions for t and t^2: // sum(t) for t from 1 to dt = dt*(dt + 1)/2 = sumOfSequence // sum(t^2) for t from 1 to dt = dt*(dt+1)*(2dt + 1)/6 = sumOfSquares // so result will be: (k-p)^2 * sumOfSquares + 2(k-p)(b-q)*sumOfSequence + dt*(b-q)^2 int256 K = (tick1 - tick0) - (avgTick1 - avgTick0); // (k - p)*dt int256 B = (tick0 - avgTick0) * dt; // (b - q)*dt int256 sumOfSquares = (dt * (dt + 1) * (2 * dt + 1)); // sumOfSquares * 6 int256 sumOfSequence = (dt * (dt + 1)); // sumOfSequence * 2 volatility = uint256((K**2 * sumOfSquares + 6 * B * K * sumOfSequence + 6 * dt * B**2) / (6 * dt**2)); } /// @notice Transforms a previous timepoint into a new timepoint, given the passage of time and the current tick and liquidity values /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows /// @param last The specified timepoint to be used in creation of new timepoint /// @param blockTimestamp The timestamp of the new timepoint /// @param tick The active tick at the time of the new timepoint /// @param prevTick The active tick at the time of the last timepoint /// @param liquidity The total in-range liquidity at the time of the new timepoint /// @param averageTick The average tick at the time of the new timepoint /// @param volumePerLiquidity The gmean(volumes)/liquidity at the time of the new timepoint /// @return Timepoint The newly populated timepoint function createNewTimepoint( Timepoint memory last, uint32 blockTimestamp, int24 tick, int24 prevTick, uint128 liquidity, int24 averageTick, uint128 volumePerLiquidity ) private pure returns (Timepoint memory) { uint32 delta = blockTimestamp - last.blockTimestamp; last.initialized = true; last.blockTimestamp = blockTimestamp; last.tickCumulative += int56(tick) * delta; last.secondsPerLiquidityCumulative += ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)); // just timedelta if liquidity == 0 last.volatilityCumulative += uint88(_volatilityOnRange(delta, prevTick, tick, last.averageTick, averageTick)); // always fits 88 bits last.averageTick = averageTick; last.volumePerLiquidityCumulative += volumePerLiquidity; return last; } /// @notice comparator for 32-bit timestamps /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to currentTime /// @param a A comparison timestamp from which to determine the relative position of `currentTime` /// @param b From which to determine the relative position of `currentTime` /// @param currentTime A timestamp truncated to 32 bits /// @return res Whether `a` is chronologically <= `b` function lteConsideringOverflow( uint32 a, uint32 b, uint32 currentTime ) private pure returns (bool res) { res = a > currentTime; if (res == b > currentTime) res = a <= b; // if both are on the same side } /// @dev guaranteed that the result is within the bounds of int24 /// returns int256 for fuzzy tests function _getAverageTick( Timepoint[UINT16_MODULO] storage self, uint32 time, int24 tick, uint16 index, uint16 oldestIndex, uint32 lastTimestamp, int56 lastTickCumulative ) internal view returns (int256 avgTick) { uint32 oldestTimestamp = self[oldestIndex].blockTimestamp; int56 oldestTickCumulative = self[oldestIndex].tickCumulative; if (lteConsideringOverflow(oldestTimestamp, time - WINDOW, time)) { if (lteConsideringOverflow(lastTimestamp, time - WINDOW, time)) { index -= 1; // considering underflow Timepoint storage startTimepoint = self[index]; avgTick = startTimepoint.initialized ? (lastTickCumulative - startTimepoint.tickCumulative) / (lastTimestamp - startTimepoint.blockTimestamp) : tick; } else { Timepoint memory startOfWindow = getSingleTimepoint(self, time, WINDOW, tick, index, oldestIndex, 0); // current-WINDOW last current // _________*____________*_______*_ // |||||||||||| avgTick = (lastTickCumulative - startOfWindow.tickCumulative) / (lastTimestamp - time + WINDOW); } } else { avgTick = (lastTimestamp == oldestTimestamp) ? tick : (lastTickCumulative - oldestTickCumulative) / (lastTimestamp - oldestTimestamp); } } /// @notice Fetches the timepoints beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. /// The result may be the same timepoint, or adjacent timepoints. /// @dev The answer must be contained in the array, used when the target is located within the stored timepoint /// boundaries: older than the most recent timepoint and younger, or the same age as, the oldest timepoint /// @param self The stored dataStorage array /// @param time The current block.timestamp /// @param target The timestamp at which the reserved timepoint should be for /// @param lastIndex The index of the timepoint that was most recently written to the timepoints array /// @param oldestIndex The index of the oldest timepoint in the timepoints array /// @return beforeOrAt The timepoint recorded before, or at, the target /// @return atOrAfter The timepoint recorded at, or after, the target function binarySearch( Timepoint[UINT16_MODULO] storage self, uint32 time, uint32 target, uint16 lastIndex, uint16 oldestIndex ) private view returns (Timepoint storage beforeOrAt, Timepoint storage atOrAfter) { uint256 left = oldestIndex; // oldest timepoint uint256 right = lastIndex >= oldestIndex ? lastIndex : lastIndex + UINT16_MODULO; // newest timepoint considering one index overflow uint256 current = (left + right) >> 1; // "middle" point between the boundaries do { beforeOrAt = self[uint16(current)]; // checking the "middle" point between the boundaries (bool initializedBefore, uint32 timestampBefore) = (beforeOrAt.initialized, beforeOrAt.blockTimestamp); if (initializedBefore) { if (lteConsideringOverflow(timestampBefore, target, time)) { // is current point before or at `target`? atOrAfter = self[uint16(current + 1)]; // checking the next point after "middle" (bool initializedAfter, uint32 timestampAfter) = (atOrAfter.initialized, atOrAfter.blockTimestamp); if (initializedAfter) { if (lteConsideringOverflow(target, timestampAfter, time)) { // is the "next" point after or at `target`? return (beforeOrAt, atOrAfter); // the only fully correct way to finish } left = current + 1; // "next" point is before the `target`, so looking in the right half } else { // beforeOrAt is initialized and <= target, and next timepoint is uninitialized // should be impossible if initial boundaries and `target` are correct return (beforeOrAt, beforeOrAt); } } else { right = current - 1; // current point is after the `target`, so looking in the left half } } else { // we've landed on an uninitialized timepoint, keep searching higher // should be impossible if initial boundaries and `target` are correct left = current + 1; } current = (left + right) >> 1; // calculating the new "middle" point index after updating the bounds } while (true); atOrAfter = beforeOrAt; // code is unreachable, to suppress compiler warning assert(false); } /// @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 self The stored dataStorage array /// @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 oldestIndex The index of the oldest timepoint /// @param liquidity The current in-range pool liquidity /// @return targetTimepoint desired timepoint or it's approximation function getSingleTimepoint( Timepoint[UINT16_MODULO] storage self, uint32 time, uint32 secondsAgo, int24 tick, uint16 index, uint16 oldestIndex, uint128 liquidity ) internal view returns (Timepoint memory targetTimepoint) { uint32 target = time - secondsAgo; // if target is newer than last timepoint if (secondsAgo == 0 || lteConsideringOverflow(self[index].blockTimestamp, target, time)) { Timepoint memory last = self[index]; if (last.blockTimestamp == target) { return last; } else { // otherwise, we need to add new timepoint int24 avgTick = int24(_getAverageTick(self, time, tick, index, oldestIndex, last.blockTimestamp, last.tickCumulative)); int24 prevTick = tick; { if (index != oldestIndex) { Timepoint memory prevLast; Timepoint storage _prevLast = self[index - 1]; // considering index underflow prevLast.blockTimestamp = _prevLast.blockTimestamp; prevLast.tickCumulative = _prevLast.tickCumulative; prevTick = int24((last.tickCumulative - prevLast.tickCumulative) / (last.blockTimestamp - prevLast.blockTimestamp)); } } return createNewTimepoint(last, target, tick, prevTick, liquidity, avgTick, 0); } } require(lteConsideringOverflow(self[oldestIndex].blockTimestamp, target, time), 'OLD'); (Timepoint memory beforeOrAt, Timepoint memory atOrAfter) = binarySearch(self, time, target, index, oldestIndex); if (target == atOrAfter.blockTimestamp) { return atOrAfter; // we're at the right boundary } if (target != beforeOrAt.blockTimestamp) { // we're in the middle uint32 timepointTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp; uint32 targetDelta = target - beforeOrAt.blockTimestamp; // For gas savings the resulting point is written to beforeAt beforeOrAt.tickCumulative += ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / timepointTimeDelta) * targetDelta; beforeOrAt.secondsPerLiquidityCumulative += uint160( (uint256(atOrAfter.secondsPerLiquidityCumulative - beforeOrAt.secondsPerLiquidityCumulative) * targetDelta) / timepointTimeDelta ); beforeOrAt.volatilityCumulative += ((atOrAfter.volatilityCumulative - beforeOrAt.volatilityCumulative) / timepointTimeDelta) * targetDelta; beforeOrAt.volumePerLiquidityCumulative += ((atOrAfter.volumePerLiquidityCumulative - beforeOrAt.volumePerLiquidityCumulative) / timepointTimeDelta) * targetDelta; } // we're at the left boundary or at the middle return beforeOrAt; } /// @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 self The stored dataStorage array /// @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 tick * time elapsed 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( Timepoint[UINT16_MODULO] storage self, uint32 time, uint32[] memory secondsAgos, int24 tick, uint16 index, uint128 liquidity ) internal view returns ( int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, uint112[] memory volatilityCumulatives, uint256[] memory volumePerAvgLiquiditys ) { tickCumulatives = new int56[](secondsAgos.length); secondsPerLiquidityCumulatives = new uint160[](secondsAgos.length); volatilityCumulatives = new uint112[](secondsAgos.length); volumePerAvgLiquiditys = new uint256[](secondsAgos.length); uint16 oldestIndex; // check if we have overflow in the past uint16 nextIndex = index + 1; // considering overflow if (self[nextIndex].initialized) { oldestIndex = nextIndex; } Timepoint memory current; for (uint256 i = 0; i < secondsAgos.length; i++) { current = getSingleTimepoint(self, time, secondsAgos[i], tick, index, oldestIndex, liquidity); (tickCumulatives[i], secondsPerLiquidityCumulatives[i], volatilityCumulatives[i], volumePerAvgLiquiditys[i]) = ( current.tickCumulative, current.secondsPerLiquidityCumulative, current.volatilityCumulative, current.volumePerLiquidityCumulative ); } } /// @notice Returns average volatility in the range from time-WINDOW to time /// @param self The stored dataStorage array /// @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 volatilityAverage The average volatility in the recent range /// @return volumePerLiqAverage The average volume per liquidity in the recent range function getAverages( Timepoint[UINT16_MODULO] storage self, uint32 time, int24 tick, uint16 index, uint128 liquidity ) internal view returns (uint88 volatilityAverage, uint256 volumePerLiqAverage) { uint16 oldestIndex; Timepoint storage oldest = self[0]; uint16 nextIndex = index + 1; // considering overflow if (self[nextIndex].initialized) { oldest = self[nextIndex]; oldestIndex = nextIndex; } Timepoint memory endOfWindow = getSingleTimepoint(self, time, 0, tick, index, oldestIndex, liquidity); uint32 oldestTimestamp = oldest.blockTimestamp; if (lteConsideringOverflow(oldestTimestamp, time - WINDOW, time)) { Timepoint memory startOfWindow = getSingleTimepoint(self, time, WINDOW, tick, index, oldestIndex, liquidity); return ( (endOfWindow.volatilityCumulative - startOfWindow.volatilityCumulative) / WINDOW, uint256(endOfWindow.volumePerLiquidityCumulative - startOfWindow.volumePerLiquidityCumulative) >> 57 ); } else if (time != oldestTimestamp) { uint88 _oldestVolatilityCumulative = oldest.volatilityCumulative; uint144 _oldestVolumePerLiquidityCumulative = oldest.volumePerLiquidityCumulative; return ( (endOfWindow.volatilityCumulative - _oldestVolatilityCumulative) / (time - oldestTimestamp), uint256(endOfWindow.volumePerLiquidityCumulative - _oldestVolumePerLiquidityCumulative) >> 57 ); } } /// @notice Initialize the dataStorage array by writing the first slot. Called once for the lifecycle of the timepoints array /// @param self The stored dataStorage array /// @param time The time of the dataStorage initialization, via block.timestamp truncated to uint32 /// @param tick Initial tick function initialize( Timepoint[UINT16_MODULO] storage self, uint32 time, int24 tick ) internal { require(!self[0].initialized); self[0].initialized = true; self[0].blockTimestamp = time; self[0].averageTick = tick; } /// @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 self The stored dataStorage array /// @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( Timepoint[UINT16_MODULO] storage self, uint16 index, uint32 blockTimestamp, int24 tick, uint128 liquidity, uint128 volumePerLiquidity ) internal returns (uint16 indexUpdated) { Timepoint storage _last = self[index]; // early return if we've already written an timepoint this block if (_last.blockTimestamp == blockTimestamp) { return index; } Timepoint memory last = _last; // get next index considering overflow indexUpdated = index + 1; uint16 oldestIndex; // check if we have overflow in the past if (self[indexUpdated].initialized) { oldestIndex = indexUpdated; } int24 avgTick = int24(_getAverageTick(self, blockTimestamp, tick, index, oldestIndex, last.blockTimestamp, last.tickCumulative)); int24 prevTick = tick; if (index != oldestIndex) { Timepoint storage _prevLast = self[index - 1]; // considering index underflow uint32 _prevLastBlockTimestamp = _prevLast.blockTimestamp; int56 _prevLastTickCumulative = _prevLast.tickCumulative; prevTick = int24((last.tickCumulative - _prevLastTickCumulative) / (last.blockTimestamp - _prevLastBlockTimestamp)); } self[indexUpdated] = createNewTimepoint(last, blockTimestamp, tick, prevTick, liquidity, avgTick, volumePerLiquidity); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0; library Sqrt { /// @notice Gets the square root of the absolute value of the parameter function sqrtAbs(int256 _x) internal pure returns (uint256 result) { // get abs value int256 mask = _x >> (256 - 1); uint256 x = uint256((_x ^ mask) - mask); if (x == 0) result = 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x8) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // @dev Seven iterations should be enough. uint256 r1 = x / r; result = r < r1 ? r : r1; } } }
// 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: BUSL-1.1 pragma solidity =0.7.6; pragma abicoder v2; import '../../interfaces/IAlgebraFactory.sol'; import '../../interfaces/IAlgebraPoolDeployer.sol'; import '../../interfaces/IDataStorageOperator.sol'; import '../../libraries/AdaptiveFee.sol'; import '../../DataStorageOperator.sol'; /** * @title Algebra factory for simulation * @notice Is used to deploy pools and its dataStorages */ contract SimulationTimeFactory is IAlgebraFactory { /// @inheritdoc IAlgebraFactory address public override owner; /// @inheritdoc IAlgebraFactory address public immutable override poolDeployer; /// @inheritdoc IAlgebraFactory address public override farmingAddress; /// @inheritdoc IAlgebraFactory address public override vaultAddress; // values of constants for sigmoids in fee calculation formula AdaptiveFee.Configuration public baseFeeConfiguration = AdaptiveFee.Configuration( 3000 - Constants.BASE_FEE, // alpha1 15000 - 3000, // alpha2 360, // beta1 60000, // beta2 59, // gamma1 8500, // gamma2 0, // volumeBeta 10, // volumeGamma Constants.BASE_FEE // baseFee ); modifier onlyOwner() { require(msg.sender == owner); _; } /// @inheritdoc IAlgebraFactory mapping(address => mapping(address => address)) public override poolByPair; constructor(address _poolDeployer, address _vaultAddress) { owner = msg.sender; emit Owner(msg.sender); poolDeployer = _poolDeployer; vaultAddress = _vaultAddress; } /// @inheritdoc IAlgebraFactory function createPool(address tokenA, address tokenB) external override returns (address pool) { require(tokenA != tokenB); (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != address(0)); require(poolByPair[token0][token1] == address(0)); IDataStorageOperator dataStorage = new DataStorageOperator(computeAddress(token0, token1)); dataStorage.changeFeeConfiguration(baseFeeConfiguration); pool = IAlgebraPoolDeployer(poolDeployer).deploy(address(dataStorage), address(this), token0, token1); poolByPair[token0][token1] = pool; // to avoid future addresses comparing we are populating the mapping twice poolByPair[token1][token0] = pool; emit Pool(token0, token1, pool); } /// @inheritdoc IAlgebraFactory function setOwner(address _owner) external override onlyOwner { require(owner != _owner); emit Owner(_owner); owner = _owner; } /// @inheritdoc IAlgebraFactory function setFarmingAddress(address _farmingAddress) external override onlyOwner { require(farmingAddress != _farmingAddress); emit FarmingAddress(_farmingAddress); farmingAddress = _farmingAddress; } /// @inheritdoc IAlgebraFactory function setVaultAddress(address _vaultAddress) external override onlyOwner { require(vaultAddress != _vaultAddress); emit VaultAddress(_vaultAddress); vaultAddress = _vaultAddress; } /// @inheritdoc IAlgebraFactory function setBaseFeeConfiguration( uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 gamma1, uint16 gamma2, uint32 volumeBeta, uint16 volumeGamma, uint16 baseFee ) external override onlyOwner { require(uint256(alpha1) + uint256(alpha2) + uint256(baseFee) <= type(uint16).max, 'Max fee exceeded'); require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0'); baseFeeConfiguration = AdaptiveFee.Configuration(alpha1, alpha2, beta1, beta2, gamma1, gamma2, volumeBeta, volumeGamma, baseFee); emit FeeConfiguration(alpha1, alpha2, beta1, beta2, gamma1, gamma2, volumeBeta, volumeGamma, baseFee); } bytes32 internal constant POOL_INIT_CODE_HASH = 0x900bf8d45a06958144a51da8749d15e2a339e87243bd50bc88d46815c9ec888d; /// @notice Deterministically computes the pool address given the factory and PoolKey /// @param token0 first token /// @param token1 second token /// @return pool The contract address of the Algebra pool function computeAddress(address token0, address token1) internal view returns (address pool) { pool = address(uint256(keccak256(abi.encodePacked(hex'ff', poolDeployer, keccak256(abi.encode(token0, token1)), POOL_INIT_CODE_HASH)))); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/AdaptiveFee.sol'; import '../libraries/Constants.sol'; contract AdaptiveFeeTest { AdaptiveFee.Configuration public feeConfig = AdaptiveFee.Configuration( 3000 - Constants.BASE_FEE, // alpha1 15000 - 3000, // alpha2 360, // beta1 60000, // beta2 59, // gamma1 8500, // gamma2 0, // volumeBeta 10, // volumeGamma Constants.BASE_FEE // baseFee ); function getFee(uint88 volatility, uint256 volumePerLiquidity) external view returns (uint256 fee) { return AdaptiveFee.getFee(volatility, volumePerLiquidity, feeConfig); } function getGasCostOfGetFee(uint88 volatility, uint256 volumePerLiquidity) external view returns (uint256) { uint256 gasBefore = gasleft(); AdaptiveFee.getFee(volatility, volumePerLiquidity, feeConfig); return gasBefore - gasleft(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/FullMath.sol'; import '../libraries/TokenDeltaMath.sol'; import '../libraries/PriceMovementMath.sol'; import '../libraries/Constants.sol'; contract TokenDeltaMathEchidnaTest { function mulDivRoundingUpInvariants( uint256 x, uint256 y, uint256 z ) external pure { require(z > 0); uint256 notRoundedUp = FullMath.mulDiv(x, y, z); uint256 roundedUp = FullMath.mulDivRoundingUp(x, y, z); assert(roundedUp >= notRoundedUp); assert(roundedUp - notRoundedUp < 2); if (roundedUp - notRoundedUp == 1) { assert(mulmod(x, y, z) > 0); } else { assert(mulmod(x, y, z) == 0); } } function getNextSqrtPriceFromInputInvariants( uint160 sqrtP, uint128 liquidity, uint256 amountIn, bool zeroToOne ) external pure { uint160 sqrtQ = PriceMovementMath.getNewPriceAfterInput(sqrtP, liquidity, amountIn, zeroToOne); if (zeroToOne) { assert(sqrtQ <= sqrtP); assert(amountIn >= TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity, true)); } else { assert(sqrtQ >= sqrtP); assert(amountIn >= TokenDeltaMath.getToken1Delta(sqrtP, sqrtQ, liquidity, true)); } } function getNextSqrtPriceFromOutputInvariants( uint160 sqrtP, uint128 liquidity, uint256 amountOut, bool zeroToOne ) external pure { uint160 sqrtQ = PriceMovementMath.getNewPriceAfterOutput(sqrtP, liquidity, amountOut, zeroToOne); if (zeroToOne) { assert(sqrtQ <= sqrtP); assert(amountOut <= TokenDeltaMath.getToken1Delta(sqrtQ, sqrtP, liquidity, false)); } else { assert(sqrtQ > 0); // this has to be true, otherwise we need another require assert(sqrtQ >= sqrtP); assert(amountOut <= TokenDeltaMath.getToken0Delta(sqrtP, sqrtQ, liquidity, false)); } } function getNextSqrtPriceFromAmount0RoundingUpInvariants( uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add ) external pure { require(sqrtPX96 > 0); require(liquidity > 0); uint160 sqrtQX96; if (add) { sqrtQX96 = PriceMovementMath.getNewPriceAfterInput(sqrtPX96, liquidity, amount, true); } else { sqrtQX96 = PriceMovementMath.getNewPriceAfterOutput(sqrtPX96, liquidity, amount, false); } if (add) { assert(sqrtQX96 <= sqrtPX96); } else { assert(sqrtQX96 >= sqrtPX96); } if (amount == 0) { assert(sqrtPX96 == sqrtQX96); } } function getNextSqrtPriceFromAmount1RoundingDownInvariants( uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add ) external pure { require(sqrtPX96 > 0); require(liquidity > 0); uint160 sqrtQX96; if (add) { sqrtQX96 = PriceMovementMath.getNewPriceAfterInput(sqrtPX96, liquidity, amount, false); } else { sqrtQX96 = PriceMovementMath.getNewPriceAfterOutput(sqrtPX96, liquidity, amount, true); } if (add) { assert(sqrtQX96 >= sqrtPX96); } else { assert(sqrtQX96 <= sqrtPX96); } if (amount == 0) { assert(sqrtPX96 == sqrtQX96); } } function getToken0DeltaInvariants( uint160 sqrtP, uint160 sqrtQ, uint128 liquidity ) external pure { require(sqrtP > 0 && sqrtQ > 0); if (sqrtP < sqrtQ) (sqrtP, sqrtQ) = (sqrtQ, sqrtP); uint256 amount0Down = TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity, false); uint256 amount0Up = TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity, true); assert(amount0Down <= amount0Up); // diff is 0 or 1 assert(amount0Up - amount0Down < 2); } // ensure that chained division is always equal to the full-precision case for // liquidity * (sqrt(P) - sqrt(Q)) / (sqrt(P) * sqrt(Q)) function getToken0DeltaEquivalency( uint160 sqrtP, uint160 sqrtQ, uint128 liquidity, bool roundUp ) external pure { require(sqrtP >= sqrtQ); require(sqrtP > 0 && sqrtQ > 0); require((sqrtP * sqrtQ) / sqrtP == sqrtQ); uint256 numerator1 = uint256(liquidity) << Constants.RESOLUTION; uint256 numerator2 = sqrtP - sqrtQ; uint256 denominator = uint256(sqrtP) * sqrtQ; uint256 safeResult = roundUp ? FullMath.mulDivRoundingUp(numerator1, numerator2, denominator) : FullMath.mulDiv(numerator1, numerator2, denominator); uint256 fullResult = TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity, roundUp); assert(safeResult == fullResult); } function getToken1DeltaInvariants( uint160 sqrtP, uint160 sqrtQ, uint128 liquidity ) external pure { require(sqrtP > 0 && sqrtQ > 0); if (sqrtP > sqrtQ) (sqrtP, sqrtQ) = (sqrtQ, sqrtP); uint256 amount1Down = TokenDeltaMath.getToken1Delta(sqrtP, sqrtQ, liquidity, false); uint256 amount1Up = TokenDeltaMath.getToken1Delta(sqrtP, sqrtQ, liquidity, true); assert(amount1Down <= amount1Up); // diff is 0 or 1 assert(amount1Up - amount1Down < 2); } function getToken0DeltaSignedInvariants( uint160 sqrtP, uint160 sqrtQ, int128 liquidity ) external pure { require(sqrtP > 0 && sqrtQ > 0); int256 amount0 = TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity); if (liquidity < 0) assert(amount0 <= 0); if (liquidity > 0) { if (sqrtP == sqrtQ) assert(amount0 == 0); else assert(amount0 > 0); } if (liquidity == 0) assert(amount0 == 0); } function getToken1DeltaSignedInvariants( uint160 sqrtP, uint160 sqrtQ, int128 liquidity ) external pure { require(sqrtP > 0 && sqrtQ > 0); int256 amount1 = TokenDeltaMath.getToken1Delta(sqrtP, sqrtQ, liquidity); if (liquidity < 0) assert(amount1 <= 0); if (liquidity > 0) { if (sqrtP == sqrtQ) assert(amount1 == 0); else assert(amount1 > 0); } if (liquidity == 0) assert(amount1 == 0); } function getOutOfRangeMintInvariants( uint160 sqrtA, uint160 sqrtB, int128 liquidity ) external pure { require(sqrtA > 0 && sqrtB > 0); require(liquidity > 0); int256 amount0 = TokenDeltaMath.getToken0Delta(sqrtA, sqrtB, liquidity); int256 amount1 = TokenDeltaMath.getToken1Delta(sqrtA, sqrtB, liquidity); if (sqrtA == sqrtB) { assert(amount0 == 0); assert(amount1 == 0); } else { assert(amount0 > 0); assert(amount1 > 0); } } function getInRangeMintInvariants( uint160 sqrtLower, uint160 sqrtCurrent, uint160 sqrtUpper, int128 liquidity ) external pure { require(sqrtLower > 0); require(sqrtLower < sqrtUpper); require(sqrtLower <= sqrtCurrent && sqrtCurrent <= sqrtUpper); require(liquidity > 0); int256 amount0 = TokenDeltaMath.getToken0Delta(sqrtCurrent, sqrtUpper, liquidity); int256 amount1 = TokenDeltaMath.getToken1Delta(sqrtLower, sqrtCurrent, liquidity); assert(amount0 > 0 || amount1 > 0); } }
// 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: 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.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: 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: UNLICENSED pragma solidity =0.7.6; import '../libraries/TokenDeltaMath.sol'; import '../libraries/PriceMovementMath.sol'; contract TokenDeltaMathTest { function getNewPriceAfterInput( uint160 sqrtP, uint128 liquidity, uint256 amountIn, bool zeroToOne ) external pure returns (uint160 sqrtQ) { return PriceMovementMath.getNewPriceAfterInput(sqrtP, liquidity, amountIn, zeroToOne); } function getGasCostOfGetNewPriceAfterInput( uint160 sqrtP, uint128 liquidity, uint256 amountIn, bool zeroToOne ) external view returns (uint256) { uint256 gasBefore = gasleft(); PriceMovementMath.getNewPriceAfterInput(sqrtP, liquidity, amountIn, zeroToOne); return gasBefore - gasleft(); } function getNewPriceAfterOutput( uint160 sqrtP, uint128 liquidity, uint256 amountOut, bool zeroToOne ) external pure returns (uint160 sqrtQ) { return PriceMovementMath.getNewPriceAfterOutput(sqrtP, liquidity, amountOut, zeroToOne); } function getGasCostOfGetNewPriceAfterOutput( uint160 sqrtP, uint128 liquidity, uint256 amountOut, bool zeroToOne ) external view returns (uint256) { uint256 gasBefore = gasleft(); PriceMovementMath.getNewPriceAfterOutput(sqrtP, liquidity, amountOut, zeroToOne); return gasBefore - gasleft(); } function getToken0Delta( uint160 sqrtLower, uint160 sqrtUpper, uint128 liquidity, bool roundUp ) external pure returns (uint256 amount0) { return TokenDeltaMath.getToken0Delta(sqrtLower, sqrtUpper, liquidity, roundUp); } function getToken1Delta( uint160 sqrtLower, uint160 sqrtUpper, uint128 liquidity, bool roundUp ) external pure returns (uint256 amount1) { return TokenDeltaMath.getToken1Delta(sqrtLower, sqrtUpper, liquidity, roundUp); } function getGasCostOfGetToken0Delta( uint160 sqrtLower, uint160 sqrtUpper, uint128 liquidity, bool roundUp ) external view returns (uint256) { uint256 gasBefore = gasleft(); TokenDeltaMath.getToken0Delta(sqrtLower, sqrtUpper, liquidity, roundUp); return gasBefore - gasleft(); } function getGasCostOfGetToken1Delta( uint160 sqrtLower, uint160 sqrtUpper, uint128 liquidity, bool roundUp ) external view returns (uint256) { uint256 gasBefore = gasleft(); TokenDeltaMath.getToken1Delta(sqrtLower, sqrtUpper, liquidity, roundUp); return gasBefore - gasleft(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/FullMath.sol'; contract UnsafeMathEchidnaTest { function checkDivRoundingUp(uint256 x, uint256 d) external pure { require(d > 0); uint256 z = FullMath.divRoundingUp(x, d); uint256 diff = z - (x / d); if (x % d == 0) { assert(diff == 0); } else { assert(diff == 1); } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import '../libraries/FullMath.sol'; import '../libraries/Constants.sol'; /// @title Liquidity amount functions /// @notice Provides functions for computing liquidity amounts from token amounts and prices library LiquidityAmounts { /// @notice Downcasts uint256 to uint128 /// @param x The uint258 to be downcasted /// @return y The passed value, downcasted to uint128 function toUint128(uint256 x) private pure returns (uint128 y) { require((y = uint128(x)) == x); } /// @notice Computes the amount of liquidity received for a given amount of token0 and price range /// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower)) /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary /// @param amount0 The amount0 being sent in /// @return liquidity The amount of returned liquidity function getLiquidityForAmount0( uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint256 amount0 ) internal pure returns (uint128 liquidity) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, Constants.Q96); return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96)); } /// @notice Computes the amount of liquidity received for a given amount of token1 and price range /// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)). /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary /// @param amount1 The amount1 being sent in /// @return liquidity The amount of returned liquidity function getLiquidityForAmount1( uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint256 amount1 ) internal pure returns (uint128 liquidity) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); return toUint128(FullMath.mulDiv(amount1, Constants.Q96, sqrtRatioBX96 - sqrtRatioAX96)); } /// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current /// pool prices and the prices at the tick boundaries /// @param sqrtRatioX96 A sqrt price representing the current pool prices /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary /// @param amount0 The amount of token0 being sent in /// @param amount1 The amount of token1 being sent in /// @return liquidity The maximum amount of liquidity received function getLiquidityForAmounts( uint160 sqrtRatioX96, uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint256 amount0, uint256 amount1 ) internal pure returns (uint128 liquidity) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); if (sqrtRatioX96 <= sqrtRatioAX96) { liquidity = getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0); } else if (sqrtRatioX96 < sqrtRatioBX96) { uint128 liquidity0 = getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0); uint128 liquidity1 = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1); liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1; } else { liquidity = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1); } } /// @notice Computes the amount of token0 for a given amount of liquidity and a price range /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary /// @param liquidity The liquidity being valued /// @return amount0 The amount of token0 function getAmount0ForLiquidity( uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity ) internal pure returns (uint256 amount0) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); return FullMath.mulDiv(uint256(liquidity) << Constants.RESOLUTION, sqrtRatioBX96 - sqrtRatioAX96, sqrtRatioBX96) / sqrtRatioAX96; } /// @notice Computes the amount of token1 for a given amount of liquidity and a price range /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary /// @param liquidity The liquidity being valued /// @return amount1 The amount of token1 function getAmount1ForLiquidity( uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity ) internal pure returns (uint256 amount1) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, Constants.Q96); } /// @notice Computes the token0 and token1 value for a given amount of liquidity, the current /// pool prices and the prices at the tick boundaries /// @param sqrtRatioX96 A sqrt price representing the current pool prices /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary /// @param liquidity The liquidity being valued /// @return amount0 The amount of token0 /// @return amount1 The amount of token1 function getAmountsForLiquidity( uint160 sqrtRatioX96, uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity ) internal pure returns (uint256 amount0, uint256 amount1) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); if (sqrtRatioX96 <= sqrtRatioAX96) { amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); } else if (sqrtRatioX96 < sqrtRatioBX96) { amount0 = getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity); amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity); } else { amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../interfaces/IERC20Minimal.sol'; import '../libraries/SafeCast.sol'; import '../libraries/TickMath.sol'; import '../interfaces/callback/IAlgebraMintCallback.sol'; import '../interfaces/callback/IAlgebraSwapCallback.sol'; import '../interfaces/callback/IAlgebraFlashCallback.sol'; import '../interfaces/IAlgebraPool.sol'; import './LiquidityAmounts.sol'; contract TestAlgebraCallee is IAlgebraMintCallback, IAlgebraSwapCallback, IAlgebraFlashCallback { using SafeCast for uint256; function swapExact0For1( address pool, uint256 amount0In, address recipient, uint160 limitSqrtPrice ) external { IAlgebraPool(pool).swap(recipient, true, amount0In.toInt256(), limitSqrtPrice, abi.encode(msg.sender)); } function swapExact0For1SupportingFee( address pool, uint256 amount0In, address recipient, uint160 limitSqrtPrice ) external { IAlgebraPool(pool).swapSupportingFeeOnInputTokens(msg.sender, recipient, true, amount0In.toInt256(), limitSqrtPrice, abi.encode(msg.sender)); } function swap0ForExact1( address pool, uint256 amount1Out, address recipient, uint160 limitSqrtPrice ) external { IAlgebraPool(pool).swap(recipient, true, -amount1Out.toInt256(), limitSqrtPrice, abi.encode(msg.sender)); } function swapExact1For0( address pool, uint256 amount1In, address recipient, uint160 limitSqrtPrice ) external { IAlgebraPool(pool).swap(recipient, false, amount1In.toInt256(), limitSqrtPrice, abi.encode(msg.sender)); } function swapExact1For0SupportingFee( address pool, uint256 amount1In, address recipient, uint160 limitSqrtPrice ) external { IAlgebraPool(pool).swapSupportingFeeOnInputTokens(msg.sender, recipient, false, amount1In.toInt256(), limitSqrtPrice, abi.encode(msg.sender)); } function swap1ForExact0( address pool, uint256 amount0Out, address recipient, uint160 limitSqrtPrice ) external { IAlgebraPool(pool).swap(recipient, false, -amount0Out.toInt256(), limitSqrtPrice, abi.encode(msg.sender)); } function swapToLowerSqrtPrice( address pool, uint160 price, address recipient ) external { IAlgebraPool(pool).swap(recipient, true, type(int256).max, price, abi.encode(msg.sender)); } function swapToHigherSqrtPrice( address pool, uint160 price, address recipient ) external { IAlgebraPool(pool).swap(recipient, false, type(int256).max, price, abi.encode(msg.sender)); } event SwapCallback(int256 amount0Delta, int256 amount1Delta); function algebraSwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata data ) external override { address sender = abi.decode(data, (address)); emit SwapCallback(amount0Delta, amount1Delta); if (amount0Delta > 0) { IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta)); } else if (amount1Delta > 0) { IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta)); } else { // if both are not gt 0, both must be 0. assert(amount0Delta == 0 && amount1Delta == 0); } } event MintResult(uint256 amount0Owed, uint256 amount1Owed, uint256 resultLiquidity); function mint( address pool, address recipient, int24 bottomTick, int24 topTick, uint128 amount ) external returns ( uint256 amount0Owed, uint256 amount1Owed, uint256 resultLiquidity ) { (amount0Owed, amount1Owed, resultLiquidity) = IAlgebraPool(pool).mint(msg.sender, recipient, bottomTick, topTick, amount, abi.encode(msg.sender)); emit MintResult(amount0Owed, amount1Owed, resultLiquidity); } event MintCallback(uint256 amount0Owed, uint256 amount1Owed); function algebraMintCallback( uint256 amount0Owed, uint256 amount1Owed, bytes calldata data ) external override { address sender = abi.decode(data, (address)); if (amount0Owed > 0) IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, amount0Owed); if (amount1Owed > 0) IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, amount1Owed); emit MintCallback(amount0Owed, amount1Owed); } event FlashCallback(uint256 fee0, uint256 fee1); function flash( address pool, address recipient, uint256 amount0, uint256 amount1, uint256 pay0, uint256 pay1 ) external { IAlgebraPool(pool).flash(recipient, amount0, amount1, abi.encode(msg.sender, pay0, pay1)); } function algebraFlashCallback( uint256 fee0, uint256 fee1, bytes calldata data ) external override { emit FlashCallback(fee0, fee1); (address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256)); if (pay0 > 0) IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, pay0); if (pay1 > 0) IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, pay1); } }
// 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 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: 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 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; 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; 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 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); /** * @notice The maximum amount of position liquidity that can use any tick in the range * @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and * also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool * @return The max amount of liquidity per tick */ function maxLiquidityPerTick() external view returns (uint128); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that can change /// @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); }
// 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 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 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 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 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 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: UNLICENSED pragma solidity =0.7.6; import '../interfaces/IERC20Minimal.sol'; import '../interfaces/callback/IAlgebraSwapCallback.sol'; import '../interfaces/callback/IAlgebraMintCallback.sol'; import '../interfaces/IAlgebraPool.sol'; contract TestAlgebraSwapPay is IAlgebraSwapCallback, IAlgebraMintCallback { function swap( address pool, address recipient, bool zeroToOne, uint160 price, int256 amountSpecified, uint256 pay0, uint256 pay1 ) external { IAlgebraPool(pool).swap(recipient, zeroToOne, amountSpecified, price, abi.encode(msg.sender, pay0, pay1)); } function swapSupportingFee( address pool, address recipient, bool zeroToOne, uint160 price, int256 amountSpecified, uint256 pay0, uint256 pay1 ) external { IAlgebraPool(pool).swapSupportingFeeOnInputTokens(msg.sender, recipient, zeroToOne, amountSpecified, price, abi.encode(msg.sender, pay0, pay1)); } function algebraSwapCallback( int256, int256, bytes calldata data ) external override { (address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256)); if (pay0 > 0) { IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(pay0)); } else if (pay1 > 0) { IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(pay1)); } } function mint( address pool, address recipient, int24 bottomTick, int24 topTick, uint128 amount, uint256 pay0, uint256 pay1 ) external returns ( uint256 amount0Owed, uint256 amount1Owed, uint256 resultLiquidity ) { (amount0Owed, amount1Owed, resultLiquidity) = IAlgebraPool(pool).mint( msg.sender, recipient, bottomTick, topTick, amount, abi.encode(msg.sender, pay0, pay1) ); } function algebraMintCallback( uint256 amount0Owed, uint256 amount1Owed, bytes calldata data ) external override { (address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256)); if (amount0Owed > 0) IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, pay0); if (amount1Owed > 0) IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, pay1); } }
// 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: 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 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: 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 tickSpacing() external pure override returns (int24) { return Constants.TICK_SPACING; } /// @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 './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: BUSL-1.1 pragma solidity =0.7.6; import './Constants.sol'; 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 { require(tick % Constants.TICK_SPACING == 0, 'tick is not spaced'); // ensure that the tick is spaced tick /= Constants.TICK_SPACING; // compress tick 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) { { int24 tickSpacing = Constants.TICK_SPACING; // compress and round towards negative infinity if negative assembly { tick := sub(sdiv(tick, tickSpacing), and(slt(tick, 0), not(iszero(smod(tick, tickSpacing))))) } } 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 (uncompressAndBoundTick(tick), true); } else { tick -= int24(bitNumber); return (uncompressAndBoundTick(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 (uncompressAndBoundTick(tick), true); } else { tick += int24(255 - bitNumber); return (uncompressAndBoundTick(tick), false); } } } function uncompressAndBoundTick(int24 tick) private pure returns (int24 boundedTick) { boundedTick = tick * Constants.TICK_SPACING; if (boundedTick < TickMath.MIN_TICK) { boundedTick = TickMath.MIN_TICK; } else if (boundedTick > TickMath.MAX_TICK) { boundedTick = TickMath.MAX_TICK; } } }
// 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'); } }
// 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: UNLICENSED pragma solidity =0.7.6; import '../../AlgebraPool.sol'; // used for testing time dependent behavior contract SimulationTimeAlgebraPool is AlgebraPool { // Monday, October 5, 2020 9:00:00 AM GMT-05:00 uint256 public time = 1601906400; function advanceTime(uint256 by) external { time += by; } function _blockTimestamp() internal view override returns (uint32) { return uint32(time); } function getAverages() external view returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage) { (TWVolatilityAverage, TWVolumePerLiqAverage) = IDataStorageOperator(dataStorageOperator).getAverages( _blockTimestamp(), globalState.fee, globalState.timepointIndex, liquidity ); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import '../../interfaces/IAlgebraPoolDeployer.sol'; import './SimulationTimeAlgebraPool.sol'; contract SimulationTimePoolDeployer is IAlgebraPoolDeployer { struct Parameters { address dataStorage; address factory; address token0; address token1; } /// @inheritdoc IAlgebraPoolDeployer Parameters public override parameters; address private factory; address private owner; modifier onlyFactory() { require(msg.sender == factory); _; } modifier onlyOwner() { require(msg.sender == owner); _; } constructor() { owner = msg.sender; } /// @inheritdoc IAlgebraPoolDeployer function setFactory(address _factory) external override onlyOwner { require(_factory != address(0)); require(factory == address(0)); emit Factory(_factory); factory = _factory; } /// @inheritdoc IAlgebraPoolDeployer function deploy( address dataStorage, address _factory, address token0, address token1 ) external override onlyFactory returns (address pool) { parameters = Parameters({dataStorage: dataStorage, factory: _factory, token0: token0, token1: token1}); pool = address(new SimulationTimeAlgebraPool{salt: keccak256(abi.encode(token0, token1))}()); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; pragma abicoder v2; import '../interfaces/IAlgebraPoolDeployer.sol'; import './MockTimeAlgebraPool.sol'; import '../DataStorageOperator.sol'; contract MockTimeAlgebraPoolDeployer { struct Parameters { address dataStorage; address factory; address token0; address token1; } Parameters public parameters; event PoolDeployed(address pool); AdaptiveFee.Configuration baseFeeConfiguration = AdaptiveFee.Configuration( 3000 - Constants.BASE_FEE, // alpha1 15000 - 3000, // alpha2 360, // beta1 60000, // beta2 59, // gamma1 8500, // gamma2 0, // volumeBeta 10, // volumeGamma Constants.BASE_FEE // baseFee ); function deployMock( address factory, address token0, address token1 ) external returns (address pool) { bytes32 initCodeHash = keccak256(type(MockTimeAlgebraPool).creationCode); DataStorageOperator dataStorage = (new DataStorageOperator(computeAddress(initCodeHash, token0, token1))); dataStorage.changeFeeConfiguration(baseFeeConfiguration); parameters = Parameters({dataStorage: address(dataStorage), factory: factory, token0: token0, token1: token1}); pool = address(new MockTimeAlgebraPool{salt: keccak256(abi.encode(token0, token1))}()); emit PoolDeployed(pool); } /// @notice Deterministically computes the pool address given the factory and PoolKey /// @param token0 first token /// @param token1 second token /// @return pool The contract address of the V3 pool function computeAddress( bytes32 initCodeHash, address token0, address token1 ) internal view returns (address pool) { pool = address(uint256(keccak256(abi.encodePacked(hex'ff', address(this), keccak256(abi.encode(token0, token1)), initCodeHash)))); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../AlgebraPool.sol'; // used for testing time dependent behavior contract MockTimeAlgebraPool is AlgebraPool { // Monday, October 5, 2020 9:00:00 AM GMT-05:00 uint256 public time = 1601906400; function setTotalFeeGrowth0Token(uint256 _totalFeeGrowth0Token) external { totalFeeGrowth0Token = _totalFeeGrowth0Token; } function setTotalFeeGrowth1Token(uint256 _totalFeeGrowth1Token) external { totalFeeGrowth1Token = _totalFeeGrowth1Token; } function advanceTime(uint256 by) external { time += by; } function _blockTimestamp() internal view override returns (uint32) { return uint32(time); } function checkBlockTimestamp() external view returns (bool) { require(super._blockTimestamp() == uint32(block.timestamp)); return true; } function getAverages() external view returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage) { (TWVolatilityAverage, TWVolumePerLiqAverage) = IDataStorageOperator(dataStorageOperator).getAverages( _blockTimestamp(), globalState.fee, globalState.timepointIndex, liquidity ); } function getPrevTick() external view returns (int24 tick, int24 currentTick) { if (globalState.timepointIndex > 2) { (, uint32 lastTsmp, int56 tickCum, , , , ) = IDataStorageOperator(dataStorageOperator).timepoints(globalState.timepointIndex); (, uint32 plastTsmp, int56 ptickCum, , , , ) = IDataStorageOperator(dataStorageOperator).timepoints(globalState.timepointIndex - 1); tick = int24((tickCum - ptickCum) / (lastTsmp - plastTsmp)); } currentTick = globalState.tick; } function getFee() external view returns (uint16 fee) { return IDataStorageOperator(dataStorageOperator).getFee(_blockTimestamp(), globalState.tick, globalState.timepointIndex, liquidity); } function getKeyForPosition( address owner, int24 bottomTick, int24 topTick ) external pure returns (bytes32 key) { assembly { key := or(shl(24, or(shl(24, owner), and(bottomTick, 0xFFFFFF))), and(topTick, 0xFFFFFF)) } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.7.6; import './interfaces/IAlgebraPoolDeployer.sol'; import './AlgebraPool.sol'; contract AlgebraPoolDeployer is IAlgebraPoolDeployer { struct Parameters { address dataStorage; address factory; address token0; address token1; } /// @inheritdoc IAlgebraPoolDeployer Parameters public override parameters; address private factory; address private owner; modifier onlyFactory() { require(msg.sender == factory); _; } modifier onlyOwner() { require(msg.sender == owner); _; } constructor() { owner = msg.sender; } /// @inheritdoc IAlgebraPoolDeployer function setFactory(address _factory) external override onlyOwner { require(_factory != address(0)); require(factory == address(0)); emit Factory(_factory); factory = _factory; } /// @inheritdoc IAlgebraPoolDeployer function deploy( address dataStorage, address _factory, address token0, address token1 ) external override onlyFactory returns (address pool) { parameters = Parameters({dataStorage: dataStorage, factory: _factory, token0: token0, token1: token1}); pool = address(new AlgebraPool{salt: keccak256(abi.encode(token0, token1))}()); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/SafeCast.sol'; import '../libraries/TickMath.sol'; import '../interfaces/IERC20Minimal.sol'; import '../interfaces/callback/IAlgebraSwapCallback.sol'; import '../interfaces/IAlgebraPool.sol'; contract TestAlgebraRouter is IAlgebraSwapCallback { using SafeCast for uint256; // flash swaps for an exact amount of token0 in the output pool function swapForExact0Multi( address recipient, address poolInput, address poolOutput, uint256 amount0Out ) external { address[] memory pools = new address[](1); pools[0] = poolInput; IAlgebraPool(poolOutput).swap(recipient, false, -amount0Out.toInt256(), TickMath.MAX_SQRT_RATIO - 1, abi.encode(pools, msg.sender)); } // flash swaps for an exact amount of token1 in the output pool function swapForExact1Multi( address recipient, address poolInput, address poolOutput, uint256 amount1Out ) external { address[] memory pools = new address[](1); pools[0] = poolInput; IAlgebraPool(poolOutput).swap(recipient, true, -amount1Out.toInt256(), TickMath.MIN_SQRT_RATIO + 1, abi.encode(pools, msg.sender)); } event SwapCallback(int256 amount0Delta, int256 amount1Delta); function algebraSwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata data ) public override { emit SwapCallback(amount0Delta, amount1Delta); (address[] memory pools, address payer) = abi.decode(data, (address[], address)); if (pools.length == 1) { // get the address and amount of the token that we need to pay address tokenToBePaid = amount0Delta > 0 ? IAlgebraPool(msg.sender).token0() : IAlgebraPool(msg.sender).token1(); int256 amountToBePaid = amount0Delta > 0 ? amount0Delta : amount1Delta; bool zeroToOne = tokenToBePaid == IAlgebraPool(pools[0]).token1(); IAlgebraPool(pools[0]).swap( msg.sender, zeroToOne, -amountToBePaid, zeroToOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1, abi.encode(new address[](0), payer) ); } else { if (amount0Delta > 0) { IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(payer, msg.sender, uint256(amount0Delta)); } else { IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(payer, msg.sender, uint256(amount1Delta)); } } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../interfaces/IERC20Minimal.sol'; contract TestERC20 is IERC20Minimal { mapping(address => uint256) public override balanceOf; mapping(address => mapping(address => uint256)) public override allowance; constructor(uint256 amountToMint) { mint(msg.sender, amountToMint); } function mint(address to, uint256 amount) public { uint256 balanceNext = balanceOf[to] + amount; require(balanceNext >= amount, 'overflow balance'); balanceOf[to] = balanceNext; } function transfer(address recipient, uint256 amount) external override returns (bool) { uint256 balanceBefore = balanceOf[msg.sender]; require(balanceBefore >= amount, 'insufficient balance'); balanceOf[msg.sender] = balanceBefore - amount; uint256 balanceRecipient = balanceOf[recipient]; require(balanceRecipient + amount >= balanceRecipient, 'recipient balance overflow'); if (!isDeflationary) { balanceOf[recipient] = balanceRecipient + amount; } else { balanceOf[recipient] = balanceRecipient + (amount - (amount * 5) / 100); } emit Transfer(msg.sender, recipient, amount); return true; } function approve(address spender, uint256 amount) external override returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } bool isDeflationary = false; function setDefl() external { isDeflationary = true; } function transferFrom( address sender, address recipient, uint256 amount ) external override returns (bool) { uint256 allowanceBefore = allowance[sender][msg.sender]; require(allowanceBefore >= amount, 'allowance insufficient'); allowance[sender][msg.sender] = allowanceBefore - amount; uint256 balanceRecipient = balanceOf[recipient]; require(balanceRecipient + amount >= balanceRecipient, 'overflow balance recipient'); if (!isDeflationary) { balanceOf[recipient] = balanceRecipient + amount; } else { balanceOf[recipient] = balanceRecipient + (amount - (amount * 5) / 100); } uint256 balanceSender = balanceOf[sender]; require(balanceSender >= amount, 'underflow balance sender'); balanceOf[sender] = balanceSender - amount; emit Transfer(sender, recipient, amount); return true; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../interfaces/IERC20Minimal.sol'; import '../interfaces/callback/IAlgebraSwapCallback.sol'; import '../interfaces/IAlgebraPool.sol'; contract AlgebraPoolSwapTest is IAlgebraSwapCallback { int256 private _amount0Delta; int256 private _amount1Delta; function getSwapResult( address pool, bool zeroToOne, int256 amountSpecified, uint160 limitSqrtPrice ) external returns ( int256 amount0Delta, int256 amount1Delta, uint160 nextSqrtRatio ) { (amount0Delta, amount1Delta) = IAlgebraPool(pool).swap(address(0), zeroToOne, amountSpecified, limitSqrtPrice, abi.encode(msg.sender)); (nextSqrtRatio, , , , , , ) = IAlgebraPool(pool).globalState(); } function algebraSwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata data ) external override { address sender = abi.decode(data, (address)); if (amount0Delta > 0) { IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta)); } else if (amount1Delta > 0) { IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta)); } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/TickMath.sol'; contract TickMathTest { function getSqrtRatioAtTick(int24 tick) external pure returns (uint160) { return TickMath.getSqrtRatioAtTick(tick); } function getGasCostOfGetSqrtRatioAtTick(int24 tick) external view returns (uint256) { uint256 gasBefore = gasleft(); TickMath.getSqrtRatioAtTick(tick); return gasBefore - gasleft(); } function getTickAtSqrtRatio(uint160 price) external pure returns (int24) { return TickMath.getTickAtSqrtRatio(price); } function getGasCostOfGetTickAtSqrtRatio(uint160 price) external view returns (uint256) { uint256 gasBefore = gasleft(); TickMath.getTickAtSqrtRatio(price); return gasBefore - gasleft(); } function MIN_SQRT_RATIO() external pure returns (uint160) { return TickMath.MIN_SQRT_RATIO; } function MAX_SQRT_RATIO() external pure returns (uint160) { return TickMath.MAX_SQRT_RATIO; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/TickMath.sol'; contract TickMathEchidnaTest { // uniqueness and increasing order function checkGetSqrtRatioAtTickInvariants(int24 tick) external pure { uint160 ratio = TickMath.getSqrtRatioAtTick(tick); assert(TickMath.getSqrtRatioAtTick(tick - 1) < ratio && ratio < TickMath.getSqrtRatioAtTick(tick + 1)); assert(ratio >= TickMath.MIN_SQRT_RATIO); assert(ratio <= TickMath.MAX_SQRT_RATIO); } // the ratio is always between the returned tick and the returned tick+1 function checkGetTickAtSqrtRatioInvariants(uint160 ratio) external pure { int24 tick = TickMath.getTickAtSqrtRatio(ratio); assert(ratio >= TickMath.getSqrtRatioAtTick(tick) && ratio < TickMath.getSqrtRatioAtTick(tick + 1)); assert(tick >= TickMath.MIN_TICK); assert(tick < TickMath.MAX_TICK); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/TickMath.sol'; import '../interfaces/callback/IAlgebraSwapCallback.sol'; import '../interfaces/IAlgebraPool.sol'; contract TestAlgebraReentrantCallee is IAlgebraSwapCallback { string private constant expectedReason = 'LOK'; function swapToReenter(address pool) external { IAlgebraPool(pool).swap(address(0), false, 1, TickMath.MAX_SQRT_RATIO - 1, new bytes(0)); } function algebraSwapCallback( int256, int256, bytes calldata ) external override { // try to reenter swap try IAlgebraPool(msg.sender).swap(address(0), false, 1, 0, new bytes(0)) {} catch Error(string memory reason) { require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); } // try to reenter swap supporting fee try IAlgebraPool(msg.sender).swapSupportingFeeOnInputTokens(address(0), address(0), false, 1, 0, new bytes(0)) {} catch Error( string memory reason ) { require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); } // try to reenter mint try IAlgebraPool(msg.sender).mint(address(0), address(0), 0, 0, 0, new bytes(0)) {} catch Error(string memory reason) { require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); } // try to reenter collect try IAlgebraPool(msg.sender).collect(address(0), 0, 0, 0, 0) {} catch Error(string memory reason) { require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); } // try to reenter burn try IAlgebraPool(msg.sender).burn(0, 0, 0) {} catch Error(string memory reason) { require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); } // try to reenter flash try IAlgebraPool(msg.sender).flash(address(0), 0, 0, new bytes(0)) {} catch Error(string memory reason) { require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); } require(false, 'Unable to reenter'); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/TickTable.sol'; contract TickTableTest { using TickTable for mapping(int16 => uint256); mapping(int16 => uint256) public bitmap; function toggleTick(int24 tick) external { bitmap.toggleTick(tick); } function getGasCostOfFlipTick(int24 tick) external returns (uint256) { uint256 gasBefore = gasleft(); bitmap.toggleTick(tick); return gasBefore - gasleft(); } function nextTickInTheSameRow(int24 tick, bool lte) external view returns (int24 next, bool initialized) { return bitmap.nextTickInTheSameRow(tick, lte); } function getGasCostOfNextTickInTheSameRow(int24 tick, bool lte) external view returns (uint256) { uint256 gasBefore = gasleft(); bitmap.nextTickInTheSameRow(tick, lte); return gasBefore - gasleft(); } // returns whether the given tick is initialized function isInitialized(int24 tick) external view returns (bool) { (int24 next, bool initialized) = bitmap.nextTickInTheSameRow(tick, true); return next == tick ? initialized : false; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/TickTable.sol'; contract TickTableEchidnaTest { using TickTable for mapping(int16 => uint256); mapping(int16 => uint256) private bitmap; // returns whether the given tick is initialized function isInitialized(int24 tick) private view returns (bool) { (int24 next, bool initialized) = bitmap.nextTickInTheSameRow(tick, true); return next == tick ? initialized : false; } function toggleTick(int24 tick) external { tick = (tick / 60); tick = tick * 60; require(tick >= -887272); require(tick <= 887272); require(tick % 60 == 0); bool before = isInitialized(tick); bitmap.toggleTick(tick); assert(isInitialized(tick) == !before); } function checkNextInitializedTickWithinOneWordInvariants(int24 tick, bool lte) external view { tick = (tick / 60); tick = tick * 60; require(tick % 60 == 0); require(tick >= -887272); require(tick <= 887272); (int24 next, bool initialized) = bitmap.nextTickInTheSameRow(tick, lte); if (lte) { // type(int24).min + 256 assert(next <= tick); assert(tick - next < 256 * 60); // all the ticks between the input tick and the next tick should be uninitialized for (int24 i = tick; i > next; i -= 60) { assert(!isInitialized(i)); } assert(isInitialized(next) == initialized); } else { // type(int24).max - 256 require(tick < 887272); assert(next > tick); assert(next - tick <= 256 * 60); // all the ticks between the input tick and the next tick should be uninitialized for (int24 i = tick + 60; i < next; i += 60) { assert(!isInitialized(i)); } assert(isInitialized(next) == initialized); } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/TickTable.sol'; contract BitMathTest { function mostSignificantBit(uint256 x) external pure returns (uint8 r) { return TickTable.getMostSignificantBit(x); } function getGasCostOfMostSignificantBit(uint256 x) external view returns (uint256) { uint256 gasBefore = gasleft(); TickTable.getMostSignificantBit(x); return gasBefore - gasleft(); } function leastSignificantBit(uint256 x) external pure returns (uint8 r) { return TickTable.getSingleSignificantBit(-x & x); } function getGasCostOfLeastSignificantBit(uint256 x) external view returns (uint256) { uint256 gasBefore = gasleft(); TickTable.getSingleSignificantBit(-x & x); return gasBefore - gasleft(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/TickTable.sol'; contract BitMathEchidnaTest { function mostSignificantBitInvariant(uint256 input) external pure { require(input > 0); uint8 msb = TickTable.getMostSignificantBit(input); assert(input >= (uint256(2)**msb)); assert(msb == 255 || input < uint256(2)**(msb + 1)); } function leastSignificantBitInvariant(uint256 input) external pure { require(input > 0); uint8 lsb = TickTable.getSingleSignificantBit(-input & input); assert(input & (uint256(2)**lsb) != 0); assert(input & (uint256(2)**lsb - 1) == 0); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/LowGasSafeMath.sol'; import '../libraries/SafeCast.sol'; contract SafeMathTest { function add(uint256 x, uint256 y) external pure returns (uint256 z) { return LowGasSafeMath.add(x, y); } function sub(uint256 x, uint256 y) external pure returns (uint256 z) { return LowGasSafeMath.sub(x, y); } function mul(uint256 x, uint256 y) external pure returns (uint256 z) { return LowGasSafeMath.mul(x, y); } function addInt(int256 x, int256 y) external pure returns (int256 z) { return LowGasSafeMath.add(x, y); } function subInt(int256 x, int256 y) external pure returns (int256 z) { return LowGasSafeMath.sub(x, y); } function add128(uint128 x, uint128 y) external pure returns (uint128 z) { return LowGasSafeMath.add128(x, y); } function toUint160(uint256 y) external pure returns (uint160 z) { return SafeCast.toUint160(y); } function toInt128(int256 y) external pure returns (int128 z) { return SafeCast.toInt128(y); } function toInt256(uint256 y) external pure returns (int256 z) { return SafeCast.toInt256(y); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/LowGasSafeMath.sol'; contract LowGasSafeMathEchidnaTest { function checkAdd(uint256 x, uint256 y) external pure { uint256 z = LowGasSafeMath.add(x, y); assert(z == x + y); assert(z >= x && z >= y); } function checkSub(uint256 x, uint256 y) external pure { uint256 z = LowGasSafeMath.sub(x, y); assert(z == x - y); assert(z <= x); } function checkMul(uint256 x, uint256 y) external pure { uint256 z = LowGasSafeMath.mul(x, y); assert(z == x * y); assert(x == 0 || y == 0 || (z >= x && z >= y)); } function checkAddi(int256 x, int256 y) external pure { int256 z = LowGasSafeMath.add(x, y); assert(z == x + y); assert(y < 0 ? z < x : z >= x); } function checkSubi(int256 x, int256 y) external pure { int256 z = LowGasSafeMath.sub(x, y); assert(z == x - y); assert(y < 0 ? z > x : z <= x); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; pragma abicoder v2; import '../libraries/TickManager.sol'; contract TickTest { using TickManager for mapping(int24 => TickManager.Tick); mapping(int24 => TickManager.Tick) public ticks; function setTick(int24 tick, TickManager.Tick memory data) external { ticks[tick] = data; } function getInnerFeeGrowth( int24 bottomTick, int24 topTick, int24 currentTick, uint256 totalFeeGrowth0Token, uint256 totalFeeGrowth1Token ) external view returns (uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token) { return ticks.getInnerFeeGrowth(bottomTick, topTick, currentTick, totalFeeGrowth0Token, totalFeeGrowth1Token); } function update( int24 tick, int24 currentTick, int128 liquidityDelta, uint256 totalFeeGrowth0Token, uint256 totalFeeGrowth1Token, uint160 secondsPerLiquidityCumulative, int56 tickCumulative, uint32 time, bool upper ) external returns (bool flipped) { return ticks.update( tick, currentTick, liquidityDelta, totalFeeGrowth0Token, totalFeeGrowth1Token, secondsPerLiquidityCumulative, tickCumulative, time, upper ); } function clear(int24 tick) external { delete ticks[tick]; } function cross( int24 tick, uint256 totalFeeGrowth0Token, uint256 totalFeeGrowth1Token, uint160 secondsPerLiquidityCumulative, int56 tickCumulative, uint32 time ) external returns (int128 liquidityDelta) { return ticks.cross(tick, totalFeeGrowth0Token, totalFeeGrowth1Token, secondsPerLiquidityCumulative, tickCumulative, time); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/TickManager.sol'; contract TickOverflowSafetyEchidnaTest { using TickManager for mapping(int24 => TickManager.Tick); int24 private constant MIN_TICK = -16; int24 private constant MAX_TICK = 16; uint128 private constant MAX_LIQUIDITY = type(uint128).max / 32; mapping(int24 => TickManager.Tick) private ticks; int24 private tick = 0; // used to track how much total liquidity has been added. should never be negative int256 totalLiquidity = 0; // half the cap of fee growth has happened, this can overflow uint256 private totalFeeGrowth0Token = type(uint256).max / 2; uint256 private totalFeeGrowth1Token = type(uint256).max / 2; // how much total growth has happened, this cannot overflow uint256 private totalGrowth0 = 0; uint256 private totalGrowth1 = 0; function increaseTotalFeeGrowth0Token(uint256 amount) external { require(totalGrowth0 + amount > totalGrowth0); // overflow check totalFeeGrowth0Token += amount; // overflow desired totalGrowth0 += amount; } function increaseTotalFeeGrowth1Token(uint256 amount) external { require(totalGrowth1 + amount > totalGrowth1); // overflow check totalFeeGrowth1Token += amount; // overflow desired totalGrowth1 += amount; } function setPosition( int24 bottomTick, int24 topTick, int128 liquidityDelta ) external { require(bottomTick > MIN_TICK); require(topTick < MAX_TICK); require(bottomTick < topTick); bool flippedLower = ticks.update( bottomTick, tick, liquidityDelta, totalFeeGrowth0Token, totalFeeGrowth1Token, 0, 0, uint32(block.timestamp), false ); bool flippedUpper = ticks.update(topTick, tick, liquidityDelta, totalFeeGrowth0Token, totalFeeGrowth1Token, 0, 0, uint32(block.timestamp), true); if (flippedLower) { if (liquidityDelta < 0) { assert(ticks[bottomTick].liquidityTotal == 0); delete ticks[bottomTick]; } else assert(ticks[bottomTick].liquidityTotal > 0); } if (flippedUpper) { if (liquidityDelta < 0) { assert(ticks[topTick].liquidityTotal == 0); delete ticks[topTick]; } else assert(ticks[topTick].liquidityTotal > 0); } totalLiquidity += liquidityDelta; // requires should have prevented this assert(totalLiquidity >= 0); if (totalLiquidity == 0) { totalGrowth0 = 0; totalGrowth1 = 0; } } function moveToTick(int24 target) external { require(target > MIN_TICK); require(target < MAX_TICK); while (tick != target) { if (tick < target) { if (ticks[tick + 1].liquidityTotal > 0) ticks.cross(tick + 1, totalFeeGrowth0Token, totalFeeGrowth1Token, 0, 0, uint32(block.timestamp)); tick++; } else { if (ticks[tick].liquidityTotal > 0) ticks.cross(tick, totalFeeGrowth0Token, totalFeeGrowth1Token, 0, 0, uint32(block.timestamp)); tick--; } } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/LiquidityMath.sol'; contract LiquidityMathTest { function addDelta(uint128 x, int128 y) external pure returns (uint128 z) { return LiquidityMath.addDelta(x, y); } function getGasCostOfAddDelta(uint128 x, int128 y) external view returns (uint256) { uint256 gasBefore = gasleft(); LiquidityMath.addDelta(x, y); return gasBefore - gasleft(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../interfaces/IAlgebraVirtualPool.sol'; contract MockTimeVirtualPool is IAlgebraVirtualPool { uint32 public timestamp; bool private isExist = true; bool private isStarted = true; int24 public currentTick; function setIsExist(bool _isExist) external { isExist = _isExist; } function setIsStarted(bool _isStarted) external { isStarted = _isStarted; } function increaseCumulative(uint32 currentTimestamp) external override returns (Status) { if (!isExist) return Status.NOT_EXIST; if (!isStarted) return Status.NOT_STARTED; timestamp = currentTimestamp; return Status.ACTIVE; } function cross(int24 nextTick, bool zeroToOne) external override { zeroToOne; require(isExist, 'Virtual pool not exist'); currentTick = nextTick; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/FullMath.sol'; contract FullMathTest { function mulDiv( uint256 x, uint256 y, uint256 z ) external pure returns (uint256) { return FullMath.mulDiv(x, y, z); } function mulDivRoundingUp( uint256 x, uint256 y, uint256 z ) external pure returns (uint256) { return FullMath.mulDivRoundingUp(x, y, z); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/FullMath.sol'; contract FullMathEchidnaTest { function checkMulDivRounding( uint256 x, uint256 y, uint256 d ) external pure { require(d > 0); uint256 ceiled = FullMath.mulDivRoundingUp(x, y, d); uint256 floored = FullMath.mulDiv(x, y, d); if (mulmod(x, y, d) > 0) { assert(ceiled - floored == 1); } else { assert(ceiled == floored); } } function checkMulDiv( uint256 x, uint256 y, uint256 d ) external pure { require(d > 0); uint256 z = FullMath.mulDiv(x, y, d); if (x == 0 || y == 0) { assert(z == 0); return; } // recompute x and y via mulDiv of the result of floor(x*y/d), should always be less than original inputs by < d uint256 x2 = FullMath.mulDiv(z, d, y); uint256 y2 = FullMath.mulDiv(z, d, x); assert(x2 <= x); assert(y2 <= y); assert(x - x2 < d); assert(y - y2 < d); } function checkMulDivRoundingUp( uint256 x, uint256 y, uint256 d ) external pure { require(d > 0); uint256 z = FullMath.mulDivRoundingUp(x, y, d); if (x == 0 || y == 0) { assert(z == 0); return; } // recompute x and y via mulDiv of the result of floor(x*y/d), should always be less than original inputs by < d uint256 x2 = FullMath.mulDiv(z, d, y); uint256 y2 = FullMath.mulDiv(z, d, x); assert(x2 >= x); assert(y2 >= y); assert(x2 - x < d); assert(y2 - y < d); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; pragma abicoder v2; import '../libraries/DataStorage.sol'; contract DataStorageTest { uint256 private constant UINT16_MODULO = 65536; using DataStorage for DataStorage.Timepoint[UINT16_MODULO]; DataStorage.Timepoint[UINT16_MODULO] public timepoints; uint32 public time; int24 public tick; uint128 public liquidity; uint16 public index; struct InitializeParams { uint32 time; int24 tick; uint128 liquidity; } function initialize(InitializeParams calldata params) external { time = params.time; tick = params.tick; liquidity = params.liquidity; timepoints.initialize(params.time, tick); } function advanceTime(uint32 by) public { time += by; } struct UpdateParams { uint32 advanceTimeBy; int24 tick; uint128 liquidity; } // write an timepoint, then change tick and liquidity function update(UpdateParams calldata params) external { advanceTime(params.advanceTimeBy); index = timepoints.write(index, time, tick, liquidity, 0); //TODO: fix for testing tick = params.tick; liquidity = params.liquidity; } function batchUpdate(UpdateParams[] calldata params) external { // sload everything int24 _tick = tick; uint128 _liquidity = liquidity; uint16 _index = index; uint32 _time = time; for (uint256 i = 0; i < params.length; i++) { _time += params[i].advanceTimeBy; _index = timepoints.write(_index, _time, _tick, _liquidity, 0); _tick = params[i].tick; _liquidity = params[i].liquidity; } // sstore everything tick = _tick; liquidity = _liquidity; index = _index; time = _time; } function getTimepoints(uint32[] calldata secondsAgos) external view returns ( int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, uint112[] memory volatilityCumulatives, uint256[] memory volumePerAvgLiquiditys ) { return timepoints.getTimepoints(time, secondsAgos, tick, index, liquidity); } function getGasCostOfGetPoints(uint32[] calldata secondsAgos) external view returns (uint256) { (uint32 _time, int24 _tick, uint128 _liquidity, uint16 _index) = (time, tick, liquidity, index); uint256 gasBefore = gasleft(); timepoints.getTimepoints(_time, secondsAgos, _tick, _index, _liquidity); return gasBefore - gasleft(); } function volatilityOnRange( uint32 dt, int24 tick0, int24 tick1, int24 avgTick0, int24 avgTick1 ) external pure returns (uint256) { return DataStorage._volatilityOnRange(dt, tick0, tick1, avgTick0, avgTick1); } function getAverageTick() external view returns (int256) { uint32 lastTimestamp = timepoints[index].blockTimestamp; int56 lastTickCumulative = timepoints[index].tickCumulative; uint16 oldestIndex; if (timepoints[index + 1].initialized) { oldestIndex = index + 1; } (uint32 _time, int24 _tick, uint16 _index) = (time, tick, index); return timepoints._getAverageTick(_time, _tick, _index, oldestIndex, lastTimestamp, lastTickCumulative); } function window() external pure returns (uint256) { return DataStorage.WINDOW; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; pragma abicoder v2; import './DataStorageTest.sol'; contract DataStorageEchidnaTest { DataStorageTest private dataStorage; bool private initialized; uint32 private timePassed; constructor() { dataStorage = new DataStorageTest(); } function initialize( uint32 time, int24 tick, uint128 liquidity ) external { require(tick % 60 == 0); dataStorage.initialize(DataStorageTest.InitializeParams({time: time, tick: tick, liquidity: liquidity})); initialized = true; } function limitTimePassed(uint32 by) private { require(timePassed + by >= timePassed); timePassed += by; } function advanceTime(uint32 by) public { limitTimePassed(by); dataStorage.advanceTime(by); } // write an timepoint, then change tick and liquidity function update( uint32 advanceTimeBy, int24 tick, uint128 liquidity ) external { require(initialized); limitTimePassed(advanceTimeBy); dataStorage.update(DataStorageTest.UpdateParams({advanceTimeBy: advanceTimeBy, tick: tick, liquidity: liquidity})); } function checkTimeWeightedResultAssertions(uint32 secondsAgo0, uint32 secondsAgo1) private view { require(secondsAgo0 != secondsAgo1); require(initialized); // secondsAgo0 should be the larger one if (secondsAgo0 < secondsAgo1) (secondsAgo0, secondsAgo1) = (secondsAgo1, secondsAgo0); uint32 timeElapsed = secondsAgo0 - secondsAgo1; uint32[] memory secondsAgos = new uint32[](2); secondsAgos[0] = secondsAgo0; secondsAgos[1] = secondsAgo1; (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, , ) = dataStorage.getTimepoints(secondsAgos); int56 timeWeightedTick = (tickCumulatives[1] - tickCumulatives[0]) / timeElapsed; uint256 timeWeightedHarmonicMeanLiquidity = (uint256(timeElapsed) * type(uint160).max) / (uint256(secondsPerLiquidityCumulatives[1] - secondsPerLiquidityCumulatives[0]) << 32); assert(timeWeightedHarmonicMeanLiquidity <= type(uint128).max); assert(timeWeightedTick <= type(int24).max); assert(timeWeightedTick >= type(int24).min); } function echidna_indexAlwaysLtCardinality() external view returns (bool) { return dataStorage.index() < 65536 || !initialized; } function echidna_avgTickNotOverflows() external view returns (bool) { int256 res = dataStorage.getAverageTick(); return (res <= type(int24).max && res >= type(int24).min); } function echidna_canAlwaysGetPoints0IfInitialized() external view returns (bool) { if (!initialized) { return true; } uint32[] memory arr = new uint32[](1); arr[0] = 0; (bool success, ) = address(dataStorage).staticcall(abi.encodeWithSelector(DataStorageTest.getTimepoints.selector, arr)); return success; } function checkVolatilityOnRangeNotOverflowUint88( uint32 dt, int24 tick0, int24 tick1, int24 avgTick0, int24 avgTick1 ) external view { uint256 res = dataStorage.volatilityOnRange(dt, tick0, tick1, avgTick0, avgTick1); assert(res <= type(uint88).max); } function checkTwoAdjacentTimepointsTickCumulativeModTimeElapsedAlways0(uint16 index) external view { // check that the timepoints are initialized, and that the index is not the oldest timepoint require(index < 65536 && index != (dataStorage.index() + 1) % 65536); (bool initialized0, uint32 blockTimestamp0, int56 tickCumulative0, , , , ) = dataStorage.timepoints(index == 0 ? 65536 - 1 : index - 1); (bool initialized1, uint32 blockTimestamp1, int56 tickCumulative1, , , , ) = dataStorage.timepoints(index); require(initialized0); require(initialized1); uint32 timeElapsed = blockTimestamp1 - blockTimestamp0; assert(timeElapsed > 0); assert((tickCumulative1 - tickCumulative0) % timeElapsed == 0); } function checkTimeWeightedAveragesAlwaysFitsType(uint32 secondsAgo) external view { require(initialized); require(secondsAgo > 0); uint32[] memory secondsAgos = new uint32[](2); secondsAgos[0] = secondsAgo; secondsAgos[1] = 0; ( int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, //TODO: volumePerLiq , ) = dataStorage.getTimepoints(secondsAgos); // compute the time weighted tick, rounded towards negative infinity int56 numerator = tickCumulatives[1] - tickCumulatives[0]; int56 timeWeightedTick = numerator / int56(secondsAgo); if (numerator < 0 && numerator % int56(secondsAgo) != 0) { timeWeightedTick--; } // the time weighted averages fit in their respective accumulated types assert(timeWeightedTick <= type(int24).max && timeWeightedTick >= type(int24).min); uint256 timeWeightedHarmonicMeanLiquidity = (uint256(secondsAgo) * type(uint160).max) / (uint256(secondsPerLiquidityCumulatives[1] - secondsPerLiquidityCumulatives[0]) << 32); assert(timeWeightedHarmonicMeanLiquidity <= type(uint128).max); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/PriceMovementMath.sol'; contract PriceMovementMathTest { function movePriceTowardsTarget( uint160 sqrtP, uint160 sqrtPTarget, uint128 liquidity, int256 amountRemaining, uint16 feePips ) external pure returns ( uint160 sqrtQ, uint256 amountIn, uint256 amountOut, uint256 feeAmount ) { return PriceMovementMath.movePriceTowardsTarget(sqrtPTarget < sqrtP, sqrtP, sqrtPTarget, liquidity, amountRemaining, feePips); } function getGasCostOfmovePriceTowardsTarget( uint160 sqrtP, uint160 sqrtPTarget, uint128 liquidity, int256 amountRemaining, uint16 feePips ) external view returns (uint256) { uint256 gasBefore = gasleft(); PriceMovementMath.movePriceTowardsTarget(sqrtPTarget < sqrtP, sqrtP, sqrtPTarget, liquidity, amountRemaining, feePips); return gasBefore - gasleft(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/PriceMovementMath.sol'; contract PriceMovementMathEchidnaTest { function checkmovePriceTowardsTargetInvariants( uint160 sqrtPriceRaw, uint160 sqrtPriceTargetRaw, uint128 liquidity, int256 amountRemaining, uint16 feePips ) external pure { require(sqrtPriceRaw > 0); require(sqrtPriceTargetRaw > 0); require(feePips > 0); require(feePips < 1e6); (uint160 sqrtQ, uint256 amountIn, uint256 amountOut, uint256 feeAmount) = PriceMovementMath.movePriceTowardsTarget( sqrtPriceTargetRaw <= sqrtPriceRaw, sqrtPriceRaw, sqrtPriceTargetRaw, liquidity, amountRemaining, feePips ); assert(amountIn <= type(uint256).max - feeAmount); if (amountRemaining < 0) { assert(amountOut <= uint256(-amountRemaining)); } else { assert(amountIn + feeAmount <= uint256(amountRemaining)); } if (sqrtPriceRaw == sqrtPriceTargetRaw) { assert(amountIn == 0); assert(amountOut == 0); assert(feeAmount == 0); assert(sqrtQ == sqrtPriceTargetRaw); } // didn't reach price target, entire amount must be consumed if (sqrtQ != sqrtPriceTargetRaw) { if (amountRemaining < 0) assert(amountOut == uint256(-amountRemaining)); else assert(amountIn + feeAmount == uint256(amountRemaining)); } // next price is between price and price target if (sqrtPriceTargetRaw <= sqrtPriceRaw) { assert(sqrtQ <= sqrtPriceRaw); assert(sqrtQ >= sqrtPriceTargetRaw); } else { assert(sqrtQ >= sqrtPriceRaw); assert(sqrtQ <= sqrtPriceTargetRaw); } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.7.6; import '../libraries/AdaptiveFee.sol'; contract AdaptiveFeeEchidnaTest { function expInvariants(uint256 x, uint16 gamma) external pure { require(gamma != 0); if (x >= 6 * gamma) return; uint256 g8 = uint256(gamma)**8; uint256 exp = AdaptiveFee.exp(x, gamma, g8); assert(exp < 2**137); } function sigmoidInvariants( uint256 x, uint16 gamma, uint16 alpha, uint256 beta ) external pure { require(gamma != 0); uint256 res = AdaptiveFee.sigmoid(x, gamma, alpha, beta); assert(res <= type(uint16).max); assert(res <= alpha); } function getFeeInvariants( uint88 volatility, uint256 volumePerLiquidity, uint16 gamma1, uint16 gamma2, uint16 alpha1, uint16 alpha2, uint32 beta1, uint32 beta2, uint16 volumeGamma, uint32 volumeBeta, uint16 baseFee ) external pure returns (uint256 fee) { require(uint256(alpha1) + uint256(alpha2) + uint256(baseFee) <= type(uint16).max, 'Max fee exceeded'); require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0'); uint256 sigm1 = AdaptiveFee.sigmoid(volatility, gamma1, alpha1, beta1); uint256 sigm2 = AdaptiveFee.sigmoid(volatility, gamma2, alpha2, beta2); assert(sigm1 + sigm2 <= type(uint16).max); fee = baseFee + AdaptiveFee.sigmoid(volumePerLiquidity, volumeGamma, uint16(sigm1 + sigm2), volumeBeta); assert(fee <= type(uint16).max); } }
{ "optimizer": { "enabled": true, "runs": 0 }, "metadata": { "bytecodeHash": "none", "useLiteralContent": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } } }
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"},{"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":"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":"pure","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
0x608060405234801561001057600080fd5b50600436106101545760003560e01c80630dfe168114610159578063128acb081461017d57806317e25b3c1461022a5780631a6865021461024b578063289fe9b01461026f57806329047dfa14610294578063490e6cbc1461029c5780634f1eb3d814610326578063514ea4bf146103a65780636378ae441461040b57806370cf754a14610425578063713346941461042d57806374eceae6146104ca5780637c0112b7146105465780637c1fe0c81461056e578063920c34e5146105945780639d3a5241146105ef578063a34123a714610780578063aafe29c0146107ba578063c45a01551461088c578063c677e3e014610894578063d0c93a7c146108b4578063d21220a7146108d3578063e76c01e4146108db578063ecdecf4214610937578063f30dba931461093f578063f637731d146109c1578063facb0eb1146109e7575b600080fd5b6101616109ef565b604080516001600160a01b039092168252519081900360200190f35b610211600480360360a081101561019357600080fd5b6001600160a01b0382358116926020810135151592604082013592606083013516919081019060a081016080820135600160201b8111156101d357600080fd5b8201836020820111156101e557600080fd5b803590602001918460018302840111600160201b8311171561020657600080fd5b509092509050610a13565b6040805192835260208301919091528051918290030190f35b610232610c4f565b6040805163ffffffff9092168252519081900360200190f35b610253610c5b565b604080516001600160801b039092168252519081900360200190f35b6102926004803603602081101561028557600080fd5b503563ffffffff16610c6a565b005b610161610d7e565b610292600480360360808110156102b257600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b8111156102e857600080fd5b8201836020820111156102fa57600080fd5b803590602001918460018302840111600160201b8311171561031b57600080fd5b509092509050610da2565b610377600480360360a081101561033c57600080fd5b506001600160a01b03813516906020810135600290810b91604081013590910b906001600160801b0360608201358116916080013516611222565b60405180836001600160801b03168152602001826001600160801b031681526020019250505060405180910390f35b6103c3600480360360208110156103bc57600080fd5b503561141c565b604080516001600160801b03978816815263ffffffff90961660208701528581019490945260608501929092528416608084015290921660a082015290519081900360c00190f35b610413611465565b60408051918252519081900360200190f35b61025361146b565b610211600480360360c081101561044357600080fd5b6001600160a01b0382358116926020810135821692604082013515159260608301359260808101359091169181019060c0810160a0820135600160201b81111561048c57600080fd5b82018360208201111561049e57600080fd5b803590602001918460018302840111600160201b831117156104bf57600080fd5b50909250905061147e565b6104e7600480360360208110156104e057600080fd5b503561177a565b60408051971515885263ffffffff909616602088015260069490940b868601526001600160a01b0390921660608601526001600160581b0316608085015260020b60a08401526001600160901b031660c0830152519081900360e00190f35b6102926004803603604081101561055c57600080fd5b5060ff8135811691602001351661184b565b6102926004803603602081101561058457600080fd5b50356001600160a01b03166119e2565b6105be600480360360408110156105aa57600080fd5b508035600290810b9160200135900b611ad1565b6040805160069490940b84526001600160a01b03909216602084015263ffffffff1682820152519081900360600190f35b61065d6004803603602081101561060557600080fd5b810190602081018135600160201b81111561061f57600080fd5b82018360208201111561063157600080fd5b803590602001918460208302840111600160201b8311171561065257600080fd5b509092509050611d88565b6040518080602001806020018060200180602001858103855289818151815260200191508051906020019060200280838360005b838110156106a9578181015183820152602001610691565b50505050905001858103845288818151815260200191508051906020019060200280838360005b838110156106e85781810151838201526020016106d0565b50505050905001858103835287818151815260200191508051906020019060200280838360005b8381101561072757818101518382015260200161070f565b50505050905001858103825286818151815260200191508051906020019060200280838360005b8381101561076657818101518382015260200161074e565b505050509050019850505050505050505060405180910390f35b6102116004803603606081101561079657600080fd5b508035600290810b91602081013590910b90604001356001600160801b03166120ed565b610865600480360360c08110156107d057600080fd5b6001600160a01b0382358116926020810135909116916040820135600290810b92606081013590910b916001600160801b03608083013516919081019060c0810160a0820135600160201b81111561082757600080fd5b82018360208201111561083957600080fd5b803590602001918460018302840111600160201b8311171561085a57600080fd5b509092509050612329565b6040805193845260208401929092526001600160801b031682820152519081900360600190f35b610161612880565b610413600480360360208110156108aa57600080fd5b503560010b6128a4565b6108bc6128b6565b6040805160029290920b8252519081900360200190f35b6101616128bb565b6108e36128df565b604080516001600160a01b03909816885260029690960b602088015261ffff9485168787015292909316606086015260ff90811660808601529190911660a0840152151560c0830152519081900360e00190f35b610413612932565b61095f6004803603602081101561095557600080fd5b503560020b612938565b604080516001600160801b039099168952600f9790970b602089015287870195909552606087019390935260069190910b60808601526001600160a01b031660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b610292600480360360208110156109d757600080fd5b50356001600160a01b03166129a2565b610161612b13565b7f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e581565b600080600080600080610a278b8b8b612b29565b949a509298509096509450925090508a15610adc576000851215610a7357610a737f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e68d87600003613409565b6000610a7d613557565b9050610a8b87878b8b6135f7565b610a93613557565b610a9d8289613697565b1115610ad6576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b50610b78565b6000861215610b1357610b137f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e58d88600003613409565b6000610b1d6136ad565b9050610b2b87878b8b6135f7565b610b336136ad565b610b3d8288613697565b1115610b76576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b8015610bd457610bd48b610bac577f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e6610bce565b7f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e55b8261371c565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e169133916000805160206154de833981519152919081900360a00190a350506002805460ff60e81b1916600160e81b17905550919890975095505050505050565b60045463ffffffff1681565b6003546001600160801b031681565b7f0000000000000000000000004b9f4d2435ef65559567e5dbfc1bbb37abc43b576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610cc357600080fd5b505afa158015610cd7573d6000803e3d6000fd5b505050506040513d6020811015610ced57600080fd5b50516001600160a01b03163314610d0357600080fd5b6201518063ffffffff821611801590610d27575060045463ffffffff828116911614155b610d3057600080fd5b6004805463ffffffff831663ffffffff19909116811790915560408051918252517fb5e51602371b0e74f991b6e965cd7d32b4b14c7e6ede6d1298037650a0e1405f9181900360200190a150565b7f0000000000000000000000005e54affaa9e560191c52eccda37eae9fa991f03f81565b600254600160e81b900460ff16610de6576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b191690556003546001600160801b031680610e35576040805162461bcd60e51b81526020600482015260016024820152601360fa1b604482015290519081900360640190fd5b600254600160b81b900461ffff16600080610e4e613557565b90508715610e9557610e68888461ffff16620f42406137b5565b9150610e957f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e58a8a613409565b600080610ea06136ad565b90508815610ee757610eba898661ffff16620f42406137b5565b9150610ee77f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e68c8b613409565b336001600160a01b031663a60b0d3c85848b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015610f6957600080fd5b505af1158015610f7d573d6000803e3d6000fd5b5050505060007f0000000000000000000000004b9f4d2435ef65559567e5dbfc1bbb37abc43b576001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b158015610fdc57600080fd5b505afa158015610ff0573d6000803e3d6000fd5b505050506040513d602081101561100657600080fd5b505190506000611014613557565b9050806110218688613697565b1115611059576040805162461bcd60e51b8152602060048201526002602482015261046360f41b604482015290519081900360640190fd5b84900380156110d357600254600160d81b900460ff16600081156110ad57506103e860ff82168302046110ad7f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e58583613409565b6110c7818403600160801b8c6001600160801b031661382a565b60008054909101905550505b60006110dd6136ad565b9050806110ea8587613697565b1115611122576040805162461bcd60e51b8152602060048201526002602482015261463160f01b604482015290519081900360640190fd5b839003801561119c57600254600160e01b900460ff166000811561117657506103e860ff82168302046111767f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e68683613409565b611190818403600160801b8d6001600160801b031661382a565b60018054909101905550505b8d6001600160a01b0316336001600160a01b03167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a350506002805460ff60e81b1916600160e81b179055505050505050505050505050565b6002546000908190600160e81b900460ff1661126b576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560006112853388886138c0565b60038101549091506001600160801b0380821691600160801b9004811690871682106112b157866112b3565b815b9450806001600160801b0316866001600160801b0316116112d457856112d6565b805b93506001600160801b03858517161561139a576003830180546001600160801b0319168684036001600160801b03908116919091178116600160801b87850383160217909155851615611357576113577f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e58b876001600160801b0316613409565b6001600160801b0384161561139a5761139a7f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e68b866001600160801b0316613409565b604080516001600160a01b038c1681526001600160801b038088166020830152861681830152905160028a810b92908c900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a450506002805460ff60e81b1916600160e81b1790555090969095509350505050565b60076020526000908152604090208054600182015460028301546003909301546001600160801b038084169463ffffffff600160801b9586900416949092808316929190041686565b60005481565b6e023746e6a58dcb13d4af821b93f06290565b6002546000908190600160e81b900460ff166114c7576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905586156115485760006114e4613557565b90506114f387600087876135f7565b600061150782611501613557565b906138e4565b97508713611542576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b506115ac565b60006115526136ad565b905061156160008887876135f7565b600061156f826115016136ad565b975087136115aa576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b6002805460ff60e81b1916600160e81b17905560008080806115cf8b8b8b612b29565b949a509298509096509450925090508a1561165c57600085121561161b5761161b7f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e68d87600003613409565b89861215611657576116577f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e58e6116528d8a6138f4565b613409565b6116ca565b6000861215611693576116937f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e58d88600003613409565b898512156116ca576116ca7f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e68e6116528d896138f4565b80156116fe576116fe8b610bac577f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e6610bce565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e169133916000805160206154de833981519152919081900360a00190a350506002805460ff60e81b1916600160e81b1790555091999098509650505050505050565b60008060008060008060007f0000000000000000000000005e54affaa9e560191c52eccda37eae9fa991f03f6001600160a01b03166374eceae6896040518263ffffffff1660e01b81526004018082815260200191505060e06040518083038186803b1580156117e957600080fd5b505afa1580156117fd573d6000803e3d6000fd5b505050506040513d60e081101561181357600080fd5b508051602082015160408301516060840151608085015160a086015160c090960151949e939d50919b50995097509195509350915050565b600254600160e81b900460ff1661188f576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560408051638da5cb5b60e01b815290516001600160a01b037f0000000000000000000000004b9f4d2435ef65559567e5dbfc1bbb37abc43b571691638da5cb5b916004808301926020929190829003018186803b1580156118fc57600080fd5b505afa158015611910573d6000803e3d6000fd5b505050506040513d602081101561192657600080fd5b50516001600160a01b0316331461193c57600080fd5b60fa60ff831611801590611954575060fa60ff821611155b61195d57600080fd5b6002805460ff60e01b1916600160e01b60ff8481169182029290921760ff60d81b1916600160d81b9286169283021790925560408051918252602082019290925281517f9e22b964b08e25c3aaa72102bb0071c089258fb82d51271a8ddf5c24921356ee929181900390910190a150506002805460ff60e81b1916600160e81b179055565b7f0000000000000000000000004b9f4d2435ef65559567e5dbfc1bbb37abc43b576001600160a01b0316638a2ade586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a3b57600080fd5b505afa158015611a4f573d6000803e3d6000fd5b505050506040513d6020811015611a6557600080fd5b50516001600160a01b03163314611a7b57600080fd5b60048054600160201b600160c01b031916600160201b6001600160a01b038416908102919091179091556040517f915c5369e6580733735d1c2e30ca20dcaa395697a041033c9f35f80f53525e8490600090a250565b600080808484620d89e9600282900b12611b18576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13611b58576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13611b9a576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b611ba261540e565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c0a57600080fd5b50611c1361540e565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c7b57600080fd5b5060028054600160a01b8104820b91600160c81b90910461ffff16908b810b9083900b1215611cd45782600001518460000151038360200151856020015103846040015186604001510398509850985050505050611d7f565b8960020b8260020b1215611d53576000611cec61390a565b9050600080611d148360008787600360009054906101000a90046001600160801b031661390e565b5050915091508560000151876000015183030386602001518860200151830303876040015189604001518603039b509b509b5050505050505050611d7f565b836000015183600001510384602001518460200151038560400151856040015103985098509850505050505b50509250925092565b6060806060807f0000000000000000000000005e54affaa9e560191c52eccda37eae9fa991f03f6001600160a01b031663fd31e988611dc561390a565b6002805460035460405160e086901b6001600160e01b031916815263ffffffff851660048201908152600160a01b8404850b9485900b6044830152600160c81b90930461ffff16606482018190526001600160801b039092166084820181905260a06024830190815260a483018e90528e958e9590949390919060c401876020880280828437600081840152601f19601f82011690508083019250505097505050505050505060006040518083038186803b158015611e8357600080fd5b505afa158015611e97573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526080811015611ec057600080fd5b8101908080516040519392919084600160201b821115611edf57600080fd5b908301906020820185811115611ef457600080fd5b82518660208202830111600160201b82111715611f1057600080fd5b82525081516020918201928201910280838360005b83811015611f3d578181015183820152602001611f25565b5050505090500160405260200180516040519392919084600160201b821115611f6557600080fd5b908301906020820185811115611f7a57600080fd5b82518660208202830111600160201b82111715611f9657600080fd5b82525081516020918201928201910280838360005b83811015611fc3578181015183820152602001611fab565b5050505090500160405260200180516040519392919084600160201b821115611feb57600080fd5b90830190602082018581111561200057600080fd5b82518660208202830111600160201b8211171561201c57600080fd5b82525081516020918201928201910280838360005b83811015612049578181015183820152602001612031565b5050505090500160405260200180516040519392919084600160201b82111561207157600080fd5b90830190602082018581111561208657600080fd5b82518660208202830111600160201b821117156120a257600080fd5b82525081516020918201928201910280838360005b838110156120cf5781810151838201526020016120b7565b50505050905001604052505050935093509350935092959194509250565b6002546000908190600160e81b900460ff16612136576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916815585908590620d89e99082900b12612187576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b136121c7576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612209576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b600080600061222e338b8b6122268c6001600160801b03166139fe565b600003613a14565b9250925092508160000396508060000395508587176000146122af576003830154612262906001600160801b031688613cea565b600384015461228190600160801b90046001600160801b031688613cea565b6003850180546001600160801b03938416928416600160801b029316929092176001600160801b0319161790555b604080516001600160801b038a16815260208101899052808201889052905160028b810b92908d900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a450506002805460ff60e81b1916600160e81b179055509296919550909350505050565b60025460009081908190600160e81b900460ff16612374576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916815588908890620d89e99082900b126123c5576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13612405576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612447576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b6000886001600160801b03161161248a576040805162461bcd60e51b8152602060048201526002602482015261125360f21b604482015290519081900360640190fd5b6000806124c18c8c6124a48d6001600160801b03166139fe565b60028054600160a01b810490910b906001600160a01b0316613d06565b5090975095506000915081905086156124df576124dc613557565b91505b85156124f0576124ed6136ad565b90505b336001600160a01b0316633dd657c588888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561257257600080fd5b505af1158015612586573d6000803e3d6000fd5b5050505060008711156125dd5760008261259e613557565b03925082116125dd576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b851561262d576000816125ee6136ad565b039150811161262d576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b8994508682101561264f5761264c856001600160801b0316838961382a565b94505b8581101561268f57600061266d866001600160801b0316838961382a565b9050856001600160801b0316816001600160801b0316101561268d578095505b505b6000856001600160801b0316116126d6576040805162461bcd60e51b8152602060048083019190915260248201526324a4a61960e11b604482015290519081900360640190fd5b6000806126f68f8f8f6126f18b6001600160801b03166139fe565b613a14565b92509250508382995089111561273b576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b8281985088111561277b576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b5050868211156127b2576127b27f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e58f898503613409565b858111156127e7576127e77f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e68f888403613409565b8a60020b8c60020b8e6001600160a01b03167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde33898c8c60405180856001600160a01b03168152602001846001600160801b0316815260200183815260200182815260200194505050505060405180910390a450506002805460ff60e81b1916600160e81b17905550929a919950975095505050505050565b7f0000000000000000000000004b9f4d2435ef65559567e5dbfc1bbb37abc43b5781565b60066020526000908152604090205481565b603c90565b7f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e681565b600280546001600160a01b03811691600160a01b8204900b9061ffff600160b81b8204811691600160c81b81049091169060ff600160d81b8204811691600160e01b8104821691600160e81b9091041687565b60015481565b60056020526000908152604090208054600182015460028301546003909301546001600160801b03831693600160801b909304600f0b9290600681900b90600160381b81046001600160a01b031690600160d81b810463ffffffff1690600160f81b900460ff1688565b6002546001600160a01b0316156129e5576040805162461bcd60e51b8152602060048201526002602482015261414960f01b604482015290519081900360640190fd5b60006129f082613d9e565b905060006129fc61390a565b90507f0000000000000000000000005e54affaa9e560191c52eccda37eae9fa991f03f6001600160a01b031663475fb80c82846040518363ffffffff1660e01b8152600401808363ffffffff1681526020018260020b815260200192505050600060405180830381600087803b158015612a7557600080fd5b505af1158015612a89573d6000803e3d6000fd5b50506002805485820b62ffffff8116600160a01b0262ffffff60a01b1960ff60e81b196001600160a01b038b166001600160a01b0319909516851716600160e81b17161790925560408051918252602082019290925281517f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c959450908190039091019150a1505050565b600454600160201b90046001600160a01b031681565b6000806000806000806000612b3c61542e565b6002805461ffff600160b81b82048116610160850152600160c81b8204166101a084015260ff60e81b19811682556001600160a01b0381169750600160a01b810490910b955060ff600160d81b8204811691600160e01b8104821691600160e81b9091041680612bd9576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b8c612c10576040805162461bcd60e51b8152602060048201526002602482015261415360f01b604482015290519081900360640190fd5b60008d1361014085015260a084018d90526003546001600160801b03600160801b8204811660208701521696508d15612cb857886001600160a01b03168c6001600160a01b0316108015612c7157506401000276a36001600160a01b038d16115b612ca8576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60005460e0850152828452612d38565b886001600160a01b03168c6001600160a01b0316118015612cf5575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038d16105b612d2c576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60015460e08501528184525b600288810b900b610180850152612d4d61390a565b600454909550600160201b90046001600160a01b031615612e6357600060048054906101000a90046001600160a01b03166001600160a01b0316637f376059876040518263ffffffff1660e01b8152600401808263ffffffff168152602001915050602060405180830381600087803b158015612dc957600080fd5b505af1158015612ddd573d6000803e3d6000fd5b505050506040513d6020811015612df357600080fd5b505190506000816002811115612e0557fe5b1415612e235760048054600160201b600160c01b0319169055612e61565b6001816002811115612e3157fe5b1415612e44576001610120860152612e61565b6002816002811115612e5257fe5b1415612e615760026101208601525b505b6000612e80856101a00151878761018001518b8960200151614089565b9050846101a0015161ffff168161ffff1614612ebf5761ffff81166101a086015260006020860152612eb4868a838b61415d565b61ffff166101608601525b50505050612ecb61549f565b6001600160a01b0387168152612ee36006878e61425f565b15156040830152600290810b900b60208201819052612f019061435f565b6001600160a01b0390811660608301819052612f3f918e918a918e161182151514612f30578360600151612f32565b8c5b888f876101600151614679565b60c085015260a0840152608083015261014083015190975015612f9857612f6f8160c0015182608001510161483d565b8b039a50612f8e612f838260a0015161483d565b60c0840151906138f4565b60c0830152612fd0565b612fa58160a0015161483d565b8b019a50612fca612fbf8260c0015183608001510161483d565b60c084015190614853565b60c08301525b81511561300c57815160c08201516000916103e891612fee91614869565b81612ff557fe5b60c084018051929091049182900390529490940193505b6001600160801b03851615613043576130378160c00151600160801b876001600160801b031661382a565b60e08301805190910190525b80606001516001600160a01b0316876001600160a01b0316141561320a578060400151156131eb5781608001516130c75761308c836000846101800151856101a001518961390e565b50506001600160a01b03166060840152600690810b900b6040830152600160808301528b6130bc576000546130c0565b6001545b6101008301525b600082610120015160028111156130da57fe5b146131625760048054906101000a90046001600160a01b03166001600160a01b03166301342b1982602001518e6040518363ffffffff1660e01b8152600401808360020b8152602001821515815260200192505050600060405180830381600087803b15801561314957600080fd5b505af115801561315d573d6000803e3d6000fd5b505050505b60008c156131a75761319d82602001518460e001518561010001518660600151876040015189600561488d909695949392919063ffffffff16565b60000390506131dd565b6131da82602001518461010001518560e001518660600151876040015189600561488d909695949392919063ffffffff16565b90505b6131e78682614941565b9550505b8b6131fa578060200151613203565b60018160200151035b955061322e565b80516001600160a01b0388811691161461322e5761322787613d9e565b955061325b565b8a158061324c5750896001600160a01b0316876001600160a01b0316145b156132565761325b565b612ecb565b81610140015115158c15151461327c578160c001518b8360a0015103613289565b8a8260a00151038260c001515b6101608401516101a08501516002805461ffff60c81b1916600160c81b61ffff938416021761ffff60b81b1916600160b81b92909316919091029190911762ffffff60a01b1916600160a01b62ffffff8b840b1602176001600160a01b0319166001600160a01b038b81169190911790915560408051631b7297f760e11b81526001600160801b038a16600482015260248101859052604481018490529051939c50919a5087927f0000000000000000000000005e54affaa9e560191c52eccda37eae9fa991f03f909116916336e52fee916064808301926020929190829003018186803b15801561337a57600080fd5b505afa15801561338e573d6000803e3d6000fd5b505050506040513d60208110156133a457600080fd5b50516020840151600380546001600160801b03948516929093018416600160801b0293909216929092176001600160801b0319169190911790558b156133f15760e08201516000556133fa565b60e08201516001555b50505093975093979195509350565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b602083106134855780518252601f199092019160209182019101613466565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146134e7576040519150601f19603f3d011682016040523d82523d6000602084013e6134ec565b606091505b509150915081801561351a57508051158061351a575080806020019051602081101561351757600080fd5b50515b613550576040805162461bcd60e51b81526020600482015260026024820152612a2360f11b604482015290519081900360640190fd5b5050505050565b60007f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e56001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156135c657600080fd5b505afa1580156135da573d6000803e3d6000fd5b505050506040513d60208110156135f057600080fd5b5051905090565b336001600160a01b0316632c8958f6858585856040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561367957600080fd5b505af115801561368d573d6000803e3d6000fd5b5050505050505050565b808201828110156136a757600080fd5b92915050565b60007f00000000000000000000000068286607a1d43602d880d349187c3c48c0fd05e66001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156135c657600080fd5b60007f0000000000000000000000004b9f4d2435ef65559567e5dbfc1bbb37abc43b576001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561377757600080fd5b505afa15801561378b573d6000803e3d6000fd5b505050506040513d60208110156137a157600080fd5b505190506137b0838284613409565b505050565b60008315806137d0575050828202828482816137cd57fe5b04145b156137f157600082116137e257600080fd5b81810490829006151501613823565b6137fc84848461382a565b90506000828061380857fe5b848609111561382357600019811061381f57600080fd5b6001015b9392505050565b6000838302816000198587098281108382030391505080841161384c57600080fd5b8061385c57508290049050613823565b8385870960008581038616958690049560026003880281188089028203028089028203028089028203028089028203028089028203028089029091030291819003819004600101858411909403939093029190930391909104170290509392505050565b62ffffff9081169116601892831b1790911b17600090815260076020526040902090565b808203828111156136a757600080fd5b808203828113156000831215146136a757600080fd5b4290565b604080516314c5407960e01b815263ffffffff808816600483015286166024820152600285900b604482015261ffff841660648201526001600160801b038316608482015290516000918291829182916001600160a01b037f0000000000000000000000005e54affaa9e560191c52eccda37eae9fa991f03f16916314c540799160a4808301926080929190829003018186803b1580156139ae57600080fd5b505afa1580156139c2573d6000803e3d6000fd5b505050506040513d60808110156139d857600080fd5b50805160208201516040830151606090930151919c909b50919950975095505050505050565b80600f81900b8114613a0f57600080fd5b919050565b60408051606081018252600280546001600160a01b0381168352600160a01b8104820b90910b6020830152600160c81b900461ffff169181019190915260009081908190613a638888886138c0565b60008054600154929650919080600f89900b15613b2a576000613a8461390a565b9050600080613ab48360008a602001518b60400151600360009054906101000a90046001600160801b031661390e565b505091509150613ae38e89602001518e8a8a86888a600060056149f7909998979695949392919063ffffffff16565b15613af75760019450613af760068f614b92565b6020880151613b12906005908f908f8b8b87898b60016149f7565b15613b265760019350613b2660068e614b92565b5050505b60208501516000908190613b46906005908f908f908a8a614c19565b91509150613b568a8c8484614cbb565b8a600f0b600014613cd95760008b600f0b1215613bd1578315613b9f5760028d810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b8215613bd15760028c810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b6000613be88e8e8e8b602001518c60000151613d06565b919b5099509050600f81900b15613cd75760035460408901516001600160801b0390911690600090613c3b90613c1c61390a565b60208d01516003548690600160801b90046001600160801b0316614089565b90508061ffff168a6040015161ffff1614613caa57613c67613c5b61390a565b8b60200151838561415d565b6002805461ffff60b81b1916600160b81b61ffff938416021761ffff60c81b1916600160c81b92841692909202919091179055600380546001600160801b031690555b613cb4828f614941565b600380546001600160801b0319166001600160801b039290921691909117905550505b505b505050505050509450945094915050565b8082016001600160801b0380841690821610156136a757600080fd5b60008060008760020b8560020b1215613d3b57613d34613d258961435f565b613d2e8961435f565b88614e99565b9250613d93565b8660020b8560020b1215613d7557613d5684613d2e8961435f565b9250613d6b613d648961435f565b8588614ede565b9150859050613d93565b613d90613d818961435f565b613d8a8961435f565b88614ede565b91505b955095509592505050565b60006401000276a36001600160a01b03831610801590613dda575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038316105b613e0f576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b600160201b600160c01b03602083901b166001600160801b03811160071b81811c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c97908811961790941790921717909117171760808110613ea157607f810383901c9150613eab565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d607f198f0160401b60c09190911c6001603f1b161760c19b909b1c6001603e1b169a909a1760c29990991c6001603d1b169890981760c39790971c6001603c1b169690961760c49590951c6001603b1b169490941760c59390931c6001603a1b169290921760c69190911c600160391b161760c79190911c600160381b161760c89190911c600160371b161760c99190911c600160361b161760ca9190911c600160351b161760cb9190911c600160341b161760cc9190911c600160331b161760cd9190911c600160321b1617693627a301d71055774c8581026f028f6481ab7f045a5af012a19d003aa9198101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b1461407a57886001600160a01b031661405e8261435f565b6001600160a01b031611156140735781614075565b805b61407c565b815b9998505050505050505050565b60408051630eea437960e11b815261ffff8716600482015263ffffffff86166024820152600285900b60448201526001600160801b0380851660648301528316608482015290516000916001600160a01b037f0000000000000000000000005e54affaa9e560191c52eccda37eae9fa991f03f1691631dd486f29160a48082019260209290919082900301818787803b15801561412557600080fd5b505af1158015614139573d6000803e3d6000fd5b505050506040513d602081101561414f57600080fd5b505190505b95945050505050565b6040805163c53a182f60e01b815263ffffffff86166004820152600285900b602482015261ffff841660448201526001600160801b038316606482015290516000916001600160a01b037f0000000000000000000000005e54affaa9e560191c52eccda37eae9fa991f03f169163c53a182f91608480820192602092909190829003018186803b1580156141f057600080fd5b505afa158015614204573d6000803e3d6000fd5b505050506040513d602081101561421a57600080fd5b50516040805161ffff8316815290519192507f598b9f043c813aa6be3426ca60d1c65d17256312890be5118dab55b0775ebe2a919081900360200190a1949350505050565b6000806000603c9050808507151960008612168186050394505082156142f657600884901c600181810b900b60009081526020879052604090205460ff868116918282039091161b80156142d7576142b681614f0e565b60ff0360ff16870396506142c987614f51565b600194509450505050614357565b8160ff16870396506142e887614f51565b600094509450505050614357565b6001938401600881901c80860b90950b60009081526020879052604090205490949060ff861690811c801561434357614333818260000316614f89565b60ff16870196506142c987614f51565b8160ff0360ff16870196506142e887614f51565b935093915050565b6000600282810b60171d90818418829003900b620d89e88111156143ae576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b6000600182166143c257600160801b6143d4565b6ffffcb933bd6fad37aa2d162d1a5940015b6001600160881b0316905060028216156143fe576ffff97272373d413259a46990580e213a0260801c5b600482161561441d576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b600882161561443c576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b601082161561445b576fffcb9843d60f6159c9db58835c9266440260801c5b602082161561447a576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615614499576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156144b8576ffe5dee046a99a2a811c461f1969c30530260801c5b6101008216156144d8576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b6102008216156144f8576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615614518576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615614538576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615614558576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615614578576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615614598576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156145b8576f31be135f97d08fd981231505542fcfa60260801c5b620100008216156145d9576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b620200008216156145f9576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614618576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614635576b048a170391f7dc42444e8fa20260801c5b60008560020b131561465057806000198161464c57fe5b0490505b600160201b810615614663576001614666565b60005b60ff16602082901c019350505050919050565b6000806000806154db8a61468f5761509d614693565b6150ac5b90506000871261477f5760006146bb888861ffff16620f42400362ffffff16620f424061382a565b90506146cc8a8c8b8563ffffffff16565b94508481106146f9578995506146f28561ffff891662ffffff620f4240829003166137b5565b9250614758565b6147058b8a838f6150bb565b9550856001600160a01b03168a6001600160a01b03161461473b5761472f868c8b8563ffffffff16565b94508488039250614758565b6147558561ffff891662ffffff620f4240829003166137b5565b92505b614777868c8b8f61476b576150cb61476f565b6150da5b63ffffffff16565b93505061482f565b6154db8b61478f576150cb614793565b6150da5b90506147a48a8c8b8463ffffffff16565b93508760000397508388106147bb57899550614800565b6147c78b8a8a8f6150e9565b9550856001600160a01b03168a6001600160a01b0316146147f4576147f1868c8b8463ffffffff16565b93505b87841115614800578793505b61480f868c8b8563ffffffff16565b945061482b8561ffff891662ffffff620f4240829003166137b5565b9250505b509650965096509692505050565b6000600160ff1b821061484f57600080fd5b5090565b818101828112156000831215146136a757600080fd5b60008215806148845750508181028183828161488157fe5b04145b6136a757600080fd5b600286810b810b60009081526020899052604090206003810180546001600160a01b03600160381b63ffffffff600160d81b808504821689039091160263ffffffff60d81b199093169290921782810482168903909116909102600160381b600160d81b031990911617600681810b8703900b66ffffffffffffff1666ffffffffffffff199091161790559081018054860390556001810180548703905554600160801b9004600f0b979650505050505050565b60008082600f0b12156149a657826001600160801b03168260000384039150816001600160801b0316106149a1576040805162461bcd60e51b81526020600482015260026024820152614c5360f01b604482015290519081900360640190fd5b6136a7565b826001600160801b03168284019150816001600160801b031610156136a7576040805162461bcd60e51b81526020600482015260026024820152614c4160f01b604482015290519081900360640190fd5b600289810b900b600090815260208b9052604081208054600160801b8104600f0b906001600160801b031683614a2d828d614941565b90506e023746e6a58dcb13d4af821b93f0636001600160801b03821610614a80576040805162461bcd60e51b81526020600482015260026024820152614c4f60f01b604482015290519081900360640190fd5b85614aa357614a9e614a99600f85810b908f900b614853565b6139fe565b614ab7565b614ab7614a99600f85810b908f900b6138f4565b84546001600160801b03838116600f9390930b8116600160801b02918116919091176001600160801b03191682178655901595508216614b8057841594508c60020b8e60020b13614b6857600184018b9055600284018a9055600384018054600160381b600160d81b031916600160381b6001600160a01b038c16021766ffffffffffffff191666ffffffffffffff60068b900b161763ffffffff60d81b1916600160d81b63ffffffff8a16021790555b6003840180546001600160f81b0316600160f81b1790555b505050509a9950505050505050505050565b603c600282900b0760020b15614be4576040805162461bcd60e51b81526020600482015260126024820152711d1a58dac81a5cc81b9bdd081cdc1858d95960721b604482015290519081900360640190fd5b603c60029190910b05600881901c600190810b810b60009081526020939093526040909220805460ff9092169290921b189055565b600285810b810b60009081526020889052604080822087840b80850b84529183209293849391929088900b1215614c93578860020b8760020b12614c6e57816001015486039350816002015485039250614c7d565b81600101549350816002015492505b6001810154600282015494039390920391614cae565b81600101548160010154039350816002015481600201540392505b5050965096945050505050565b83546001600160801b03811690600160801b900463ffffffff16600f85900b614d26576000826001600160801b031611614d21576040805162461bcd60e51b815260206004820152600260248201526104e560f41b604482015290519081900360640190fd5b614ddf565b600085600f0b1215614d655760045463ffffffff168015614d63578063ffffffff1682614d5161390a565b0363ffffffff161015614d6357600080fd5b505b6000614d718387614941565b9050806000826001600160801b031611614d8c576000614da5565b600087600f0b13614d9d5782614da5565b614da561390a565b88546001600160801b039092166001600160801b031963ffffffff909216600160801b0263ffffffff60801b199093169290921716178755505b600186015460028701546000868314614e165760018901879055614e138388036001600160801b038716600160801b61382a565b90505b6000828714614e435760028a01879055614e408388036001600160801b038816600160801b61382a565b90505b6001600160801b038282171615614e8d5760038a0180546001600160801b031981166001600160801b039182168501821617808216600160801b9182900483168501909216021790555b50505050505050505050565b60008082600f0b1215614ec657614ebe614eb985858560000360006150f9565b61483d565b600003614ed6565b614ed6614eb985858560016150f9565b949350505050565b60008082600f0b1215614efe57614ebe614eb98585856000036000615188565b614ed6614eb98585856001615188565b600181811c909117600281901c17600481901c17600881901c17601081901c17602081901c17604081901c17608081901c179081901c900360006136a782614f89565b603c8102620d89e719600282900b1215614f705750620d89e719613a0f565b620d89e8600282900b1315613a0f5750620d89e8919050565b7f55555555555555555555555555555555555555555555555555555555555555558116156001600160801b0382161560071b176001600160401b03600160801b03600160c01b0382161560061b177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff82161560051b177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff82161560041b177eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff82161560031b177f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f82161560021b177f3333333333333333333333333333333333333333333333333333333333333333919091161560011b1790565b6000614ed68385846001615188565b6000614ed684848460016150f9565b60006141548585858560016151f9565b6000614ed683858460006150f9565b6000614ed68484846000615188565b60006141548585858560006151f9565b60006001600160a01b038585038116908516811061511657600080fd5b600160601b600160e01b03606085901b168361515757866001600160a01b031661514a8383896001600160a01b031661382a565b8161515157fe5b0461517d565b61517d61516e8383896001600160a01b03166137b5565b886001600160a01b03166153ed565b979650505050505050565b6000846001600160a01b0316846001600160a01b031610156151a957600080fd5b6001600160a01b0385850316826151d7576151d281856001600160801b0316600160601b61382a565b6151ef565b6151ef81856001600160801b0316600160601b6137b5565b9695505050505050565b600080866001600160a01b03161161521057600080fd5b6000856001600160801b03161161522657600080fd5b8115158315151415615318578361523e575084614154565b600160601b600160e01b03606086901b1682156152cc576001600160a01b0387168581029086828161526c57fe5b04141561529d5781810182811061529b57615291838a6001600160a01b0316836137b5565b9350505050614154565b505b6152c3826152be888b6001600160a01b031686816152b757fe5b0490613697565b6153ed565b92505050614154565b6001600160a01b038716858102908682816152e357fe5b04146152ee57600080fd5b8082116152fa57600080fd5b6152c3615313838a6001600160a01b03168486036137b5565b6153f8565b811561537f576153786153136001600160a01b038611156153505761534b86600160601b896001600160801b031661382a565b615368565b6001600160801b038716606087901b8161536657fe5b045b6001600160a01b03891690613697565b9050614154565b60006001600160a01b038511156153ad576153a885600160601b886001600160801b03166137b5565b6153c4565b6153c4606086901b6001600160801b0388166153ed565b905080876001600160a01b0316116153db57600080fd5b6001600160a01b038716039050614154565b808204910615150190565b806001600160a01b0381168114613a0f57600080fd5b604080516060810182526000808252602082018190529181019190915290565b604080516101c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052906101208201908152600060208201819052604082018190526060820181905260809091015290565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565bfefec42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a164736f6c6343000706000a
Deployed Bytecode Sourcemap
996:34664:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;494:40:4;;;:::i;:::-;;;;-1:-1:-1;;;;;494:40:4;;;;;;;;;;;;;;19521:1531:1;;;;;;;;;;;;;;;;-1:-1:-1;;;;;19521:1531:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;19521:1531:1;;;;;;-1:-1:-1;;;19521:1531:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;19521:1531:1;;;;;;;;;;-1:-1:-1;19521:1531:1;;-1:-1:-1;19521:1531:1;-1:-1:-1;19521:1531:1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;1112:40:5;;;:::i;:::-;;;;;;;;;;;;;;;;;;;992:33;;;:::i;:::-;;;;-1:-1:-1;;;;;992:33:5;;;;;;;;;;;;;;35347:311:1;;;;;;;;;;;;;;;;-1:-1:-1;35347:311:1;;;;:::i;:::-;;309:53:4;;;:::i;32660:1920:1:-;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;32660:1920:1;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;32660:1920:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;32660:1920:1;;;;;;;;;;-1:-1:-1;32660:1920:1;;-1:-1:-1;32660:1920:1;-1:-1:-1;32660:1920:1;:::i;16257:935::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;16257:935:1;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;16257:935:1;;;;;;;-1:-1:-1;16257:935:1;;;;:::i;:::-;;;;-1:-1:-1;;;;;16257:935:1;;;;;;;;;;;;;;;;;;;;;;;;1789:54;;;;;;;;;;;;;;;;-1:-1:-1;1789:54:1;;:::i;:::-;;;;-1:-1:-1;;;;;1789:54:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;744:44:5;;;:::i;:::-;;;;;;;;;;;;;;;;812:122:4;;;:::i;21094:2120:1:-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;21094:2120:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;21094:2120:1;;;-1:-1:-1;;;21094:2120:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;21094:2120:1;;;;;;;;;;-1:-1:-1;21094:2120:1;;-1:-1:-1;21094:2120:1;-1:-1:-1;21094:2120:1;:::i;2623:398::-;;;;;;;;;;;;;;;;-1:-1:-1;2623:398:1;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2623:398:1;;;;;;;;-1:-1:-1;;;;;2623:398:1;;;;;;;-1:-1:-1;2623:398:1;;;-1:-1:-1;;;;;2623:398:1;;;;;;;;;;;;;;34634:379;;;;;;;;;;;;;;;;-1:-1:-1;34634:379:1;;;;;;;;;;;:::i;35067:226::-;;;;;;;;;;;;;;;;-1:-1:-1;35067:226:1;-1:-1:-1;;;;;35067:226:1;;:::i;3189:2093::-;;;;;;;;;;;;;;;;-1:-1:-1;3189:2093:1;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;;;;3189:2093:1;;;;;;;;;;;;;;;;;;;;;;5329:512;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5329:512:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5329:512:1;;;;;;;;;;-1:-1:-1;5329:512:1;;-1:-1:-1;5329:512:1;-1:-1:-1;5329:512:1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17234:692;;;;;;;;;;;;;;;;-1:-1:-1;17234:692:1;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;17234:692:1;;:::i;13937:2278::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;13937:2278:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;13937:2278:1;;;;;;;;;;;;-1:-1:-1;13937:2278:1;;;-1:-1:-1;;;13937:2278:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;13937:2278:1;;;;;;;;;;-1:-1:-1;13937:2278:1;;-1:-1:-1;13937:2278:1;-1:-1:-1;13937:2278:1;:::i;:::-;;;;;;;;;;;;;;-1:-1:-1;;;;;13937:2278:1;;;;;;;;;;;;;;408:41:4;;;:::i;1368:51:5:-;;;;;;;;;;;;;;;;-1:-1:-1;1368:51:5;;;;:::i;665:102:4:-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;579:40;;;:::i;912:39:5:-;;;:::i;:::-;;;;-1:-1:-1;;;;;912:39:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;912:39:5;;;;;;;;;;;;;;;;;;828:44;;;:::i;1272:56::-;;;;;;;;;;;;;;;;-1:-1:-1;1272:56:5;;;;:::i;:::-;;;;-1:-1:-1;;;;;1272:56:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1272:56:5;;;-1:-1:-1;;;;;1272:56:5;-1:-1:-1;1272:56:5;;;;;;;;;;;;;;;;;;;;;;;;5883:494:1;;;;;;;;;;;;;;;;-1:-1:-1;5883:494:1;-1:-1:-1;;;;;5883:494:1;;:::i;1192:39:5:-;;;:::i;494:40:4:-;;;:::o;19521:1531:1:-;19689:14;19705;19727:20;19753:17;19776:24;19806:20;19998:64;20020:9;20031:14;20047;19998:21;:64::i;:::-;19918:144;;-1:-1:-1;19918:144:1;;-1:-1:-1;19918:144:1;;-1:-1:-1;19918:144:1;-1:-1:-1;19918:144:1;-1:-1:-1;19918:144:1;-1:-1:-1;20069:695:1;;;;20106:1;20096:7;:11;20092:82;;;20109:65;20137:6;20145:9;20165:7;20164:8;;20109:27;:65::i;:::-;20208:22;20233:15;:13;:15::i;:::-;20208:40;;20256:37;20270:7;20279;20288:4;;20256:13;:37::i;:::-;20391:15;:13;:15::i;:::-;20351:36;:14;20378:7;20351:18;:36::i;:::-;:55;;20343:71;;;;;-1:-1:-1;;;20343:71:1;;;;;;;;;;;;-1:-1:-1;;;20343:71:1;;;;;;;;;;;;;;;20069:695;;;;20449:1;20439:7;:11;20435:82;;;20452:65;20480:6;20488:9;20508:7;20507:8;;20452:27;:65::i;:::-;20551:22;20576:15;:13;:15::i;:::-;20551:40;;20599:37;20613:7;20622;20631:4;;20599:13;:37::i;:::-;20734:15;:13;:15::i;:::-;20694:36;:14;20721:7;20694:18;:36::i;:::-;:55;;20686:71;;;;;-1:-1:-1;;;20686:71:1;;;;;;;;;;;;-1:-1:-1;;;20686:71:1;;;;;;;;;;;;;;;20069:695;;20774:16;;20770:96;;20800:59;20817:9;:27;;20838:6;20817:27;;;20829:6;20817:27;20846:12;20800:16;:59::i;:::-;20877:90;;;;;;;;;;;;-1:-1:-1;;;;;20877:90:1;;;;;;;-1:-1:-1;;;;;20877:90:1;;;;;;;;;;;;;;;;;;;;20882:10;;-1:-1:-1;;;;;;;;;;;20877:90:1;;;;;;;;;-1:-1:-1;;20973:11:1;:27;;-1:-1:-1;;;;20973:27:1;-1:-1:-1;;;20973:27:1;;;-1:-1:-1;19521:1531:1;;;;-1:-1:-1;19521:1531:1;-1:-1:-1;;;;;;19521:1531:1:o;1112:40:5:-;;;;;;:::o;992:33::-;;;-1:-1:-1;;;;;992:33:5;;:::o;35347:311:1:-;1990:7;-1:-1:-1;;;;;1974:30:1;;:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1974:32:1;-1:-1:-1;;;;;1960:46:1;:10;:46;1952:55;;;;;;610:6:22::1;35455:56:1;::::0;::::1;;::::0;::::1;::::0;:101:::1;;-1:-1:-1::0;35515:17:1::1;::::0;:41:::1;::::0;;::::1;:17:::0;::::1;:41;;35455:101;35447:110;;;::::0;::::1;;35563:17;:40:::0;;-1:-1:-1;;35563:40:1::1;;::::0;::::1;::::0;;::::1;::::0;;;35614:39:::1;::::0;;;;;;::::1;::::0;::::1;::::0;;;;;;::::1;35347:311:::0;:::o;309:53:4:-;;;:::o;32660:1920:1:-;1570:11:5;:20;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;32819:9:1::1;::::0;-1:-1:-1;;;;;32819:9:1::1;::::0;32834:28:::1;;;::::0;;-1:-1:-1;;;32834:28:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;32834:28:1;;;;;;;;;;;;;::::1;;32883:11;:15:::0;-1:-1:-1;;;32883:15:1;::::1;;;32869:11;::::0;32948:15:::1;:13;:15::i;:::-;32923:40:::0;-1:-1:-1;32973:11:1;;32969:147:::1;;33001:45;33027:7;33036:4;33001:45;;33042:3;33001:25;:45::i;:::-;32994:52;;33054:55;33082:6;33090:9;33101:7;33054:27;:55::i;:::-;33122:12;33140:22:::0;33165:15:::1;:13;:15::i;:::-;33140:40:::0;-1:-1:-1;33190:11:1;;33186:147:::1;;33218:45;33244:7;33253:4;33218:45;;33259:3;33218:25;:45::i;:::-;33211:52;;33271:55;33299:6;33307:9;33318:7;33271:27;:55::i;:::-;33339:72;::::0;-1:-1:-1;;;33339:72:1;;::::1;::::0;::::1;::::0;;;;;;;;;;;;;;;;;;;;;;33361:10:::1;::::0;33339:54:::1;::::0;33394:4;;33400;;33406;;;;33339:72;;;;33406:4;;;;33339:72;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;33418:13;33450:7;-1:-1:-1::0;;;;;33434:37:1::1;;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;33434:39:1;;-1:-1:-1;33480:13:1::1;33496:15;:13;:15::i;:::-;33480:31:::0;-1:-1:-1;33480:31:1;33525:24:::1;:14:::0;33544:4;33525:18:::1;:24::i;:::-;:33;;33517:48;;;::::0;;-1:-1:-1;;;33517:48:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;33517:48:1;;;;;;;;;;;;;::::1;;33571:23:::0;;::::1;33605:9:::0;;33601:388:::1;;33652:11;:30:::0;-1:-1:-1;;;33652:30:1;::::1;;;33624:25;33715:23:::0;;33711:182:::1;;-1:-1:-1::0;725:4:22::1;33759:27:1;::::0;::::1;::::0;::::1;33758:67;33835:49;33863:6;33871:5:::0;33758:67;33835:27:::1;:49::i;:::-;33924:58;33940:13:::0;;::::1;-1:-1:-1::0;;;;;;;;33924:58:1;::::1;:15;:58::i;:::-;33900:20;:82:::0;;;;::::1;::::0;;-1:-1:-1;;33601:388:1::1;33995:13;34011:15;:13;:15::i;:::-;33995:31:::0;-1:-1:-1;33995:31:1;34040:24:::1;:14:::0;34059:4;34040:18:::1;:24::i;:::-;:33;;34032:48;;;::::0;;-1:-1:-1;;;34032:48:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;34032:48:1;;;;;;;;;;;;;::::1;;34086:23:::0;;::::1;34120:9:::0;;34116:388:::1;;34167:11;:30:::0;-1:-1:-1;;;34167:30:1;::::1;;;34139:25;34230:23:::0;;34226:182:::1;;-1:-1:-1::0;725:4:22::1;34274:27:1;::::0;::::1;::::0;::::1;34273:67;34350:49;34378:6;34386:5:::0;34273:67;34350:27:::1;:49::i;:::-;34439:58;34455:13:::0;;::::1;-1:-1:-1::0;;;;;;;;34439:58:1;::::1;:15;:58::i;:::-;34415:20;:82:::0;;;;::::1;::::0;;-1:-1:-1;;34116:388:1::1;34533:9;-1:-1:-1::0;;;;;34515:60:1::1;34521:10;-1:-1:-1::0;;;;;34515:60:1::1;;34544:7;34553;34562:5;34569;34515:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;-1:-1:-1;;;;;;;;;;;;32660:1920:1:o;16257:935::-;1570:11:5;:20;16434:15:1;;;;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;-1:-1:-1;16502:52:1::1;16522:10;16534::::0;16546:7;16502:19:::1;:52::i;:::-;16610:14;::::0;::::1;::::0;;;-1:-1:-1;;;;;;16610:14:1;;::::1;::::0;-1:-1:-1;;;16626:14:1;::::1;::::0;::::1;::::0;16658:32;::::1;-1:-1:-1::0;;16658:67:1::1;;16709:16;16658:67;;;16693:13;16658:67;16648:77:::0;-1:-1:-1;;;;;;16741:32:1;;::::1;::::0;;::::1;;:67;;16792:16;16741:67;;;16776:13;16741:67;16731:77:::0;-1:-1:-1;16819:17:1;;::::1;-1:-1:-1::0;;;;;16819:22:1::1;::::0;16815:292:::1;;16851:14;::::0;::::1;:40:::0;;-1:-1:-1;;;;;;16851:40:1::1;16868:23:::0;;::::1;-1:-1:-1::0;;;;;16851:40:1;;::::1;::::0;;;::::1;16899::::0;::::1;-1:-1:-1::0;;;16916:23:1;;::::1;16899:40:::0;::::1;;;::::0;;;16952:11;::::1;::::0;16948:72:::1;;16965:55;16993:6;17001:9:::0;-1:-1:-1;;;;;16965:55:1;::::1;:27;:55::i;:::-;-1:-1:-1::0;;;;;17032:11:1;::::1;::::0;17028:72:::1;;17045:55;17073:6;17081:9:::0;-1:-1:-1;;;;;17045:55:1;::::1;:27;:55::i;:::-;17118:69;::::0;;-1:-1:-1;;;;;17118:69:1;::::1;::::0;;-1:-1:-1;;;;;17118:69:1;;::::1;;::::0;::::1;::::0;;::::1;::::0;;;;;;::::1;::::0;;::::1;::::0;;;;::::1;::::0;17126:10:::1;::::0;17118:69:::1;::::0;;;;;;;;::::1;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;-1:-1:-1;16257:935:1;;;;-1:-1:-1;16257:935:1;-1:-1:-1;;;;16257:935:1:o;1789:54::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1789:54:1;;;;-1:-1:-1;;;1789:54:1;;;;;;;;;;;;;;;;;;:::o;744:44:5:-;;;;:::o;812:122:4:-;520:35:22;812:122:4;:::o;21094:2120:1:-;21521:11;:20;21308:14;;;;-1:-1:-1;;;21521:20:1;;;;21513:36;;;;;-1:-1:-1;;;21513:36:1;;;;;;;;;;;;-1:-1:-1;;;21513:36:1;;;;;;;;;;;;;;;21555:11;:28;;-1:-1:-1;;;;21555:28:1;;;21589:403;;;;21612:22;21637:15;:13;:15::i;:::-;21612:40;;21660:38;21674:14;21690:1;21693:4;;21660:13;:38::i;:::-;21779:1;21739:35;21759:14;21739:15;:13;:15::i;:::-;:19;;:35::i;:::-;21715:60;;;21714:66;21706:82;;;;;-1:-1:-1;;;21706:82:1;;;;;;;;;;;;-1:-1:-1;;;21706:82:1;;;;;;;;;;;;;;;21589:403;;;;21809:22;21834:15;:13;:15::i;:::-;21809:40;;21857:38;21871:1;21874:14;21890:4;;21857:13;:38::i;:::-;21976:1;21936:35;21956:14;21936:15;:13;:15::i;:35::-;21912:60;;;21911:66;21903:82;;;;;-1:-1:-1;;;21903:82:1;;;;;;;;;;;;-1:-1:-1;;;21903:82:1;;;;;;;;;;;;;;;21589:403;;21997:11;:27;;-1:-1:-1;;;;21997:27:1;-1:-1:-1;;;21997:27:1;;;-1:-1:-1;;;;22304:64:1;22326:9;22337:14;22353;22304:21;:64::i;:::-;22224:144;;-1:-1:-1;22224:144:1;;-1:-1:-1;22224:144:1;;-1:-1:-1;22224:144:1;-1:-1:-1;22224:144:1;-1:-1:-1;22224:144:1;-1:-1:-1;22413:513:1;;;;22450:1;22440:7;:11;22436:82;;;22453:65;22481:6;22489:9;22509:7;22508:8;;22453:27;:65::i;:::-;22570:14;22560:7;:24;22556:111;;;22586:81;22614:6;22622;22638:27;:14;22657:7;22638:18;:27::i;:::-;22586;:81::i;:::-;22413:513;;;22702:1;22692:7;:11;22688:82;;;22705:65;22733:6;22741:9;22761:7;22760:8;;22705:27;:65::i;:::-;22822:14;22812:7;:24;22808:111;;;22838:81;22866:6;22874;22890:27;:14;22909:7;22890:18;:27::i;22838:81::-;22936:16;;22932:96;;22962:59;22979:9;:27;;23000:6;22979:27;;22962:59;23039:90;;;;;;;;;;;;-1:-1:-1;;;;;23039:90:1;;;;;;;-1:-1:-1;;;;;23039:90:1;;;;;;;;;;;;;;;;;;;;23044:10;;-1:-1:-1;;;;;;;;;;;23039:90:1;;;;;;;;;-1:-1:-1;;23135:11:1;:27;;-1:-1:-1;;;;23135:27:1;-1:-1:-1;;;23135:27:1;;;-1:-1:-1;21094:2120:1;;;;-1:-1:-1;21094:2120:1;-1:-1:-1;;;;;;;21094:2120:1:o;2623:398::-;2713:16;2737:21;2766:20;2794:37;2839:27;2874:17;2899:36;2978:19;-1:-1:-1;;;;;2957:52:1;;3010:5;2957:59;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2957:59:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2957:59:1;;-1:-1:-1;2957:59:1;-1:-1:-1;2957:59:1;-1:-1:-1;2957:59:1;;-1:-1:-1;2957:59:1;-1:-1:-1;2623:398:1;-1:-1:-1;;2623:398:1:o;34634:379::-;1570:11:5;:20;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;1974:32:1::1;::::0;;-1:-1:-1;;;1974:32:1;;;;1990:7:::1;-1:-1:-1::0;;;;;1974:30:1::1;::::0;-1:-1:-1;;1974:32:1::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;:30;:32;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;1974:32:1;-1:-1:-1;;;;;1960:46:1::1;:10;:46;1952:55;;;::::0;::::1;;664:3:22::2;34756:44:1;::::0;::::2;;::::0;::::2;::::0;34755:96:::2;;-1:-1:-1::0;664:3:22::2;34806:44:1;::::0;::::2;;;34755:96;34747:105;;;::::0;::::2;;34859:11;34858:97:::0;;-1:-1:-1;;;;34858:97:1::2;-1:-1:-1::0;;;;34858:97:1;;::::2;::::0;;::::2;::::0;;;::::2;-1:-1:-1::0;;;;34858:97:1::2;-1:-1:-1::0;;;34858:97:1;;::::2;::::0;;::::2;;::::0;;;34966:42:::2;::::0;;;;;::::2;::::0;::::2;::::0;;;;;;::::2;::::0;;;;;;;;;::::2;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;34634:379:1:o;35067:226::-;35179:7;-1:-1:-1;;;;;35163:39:1;;:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;35163:41:1;-1:-1:-1;;;;;35149:55:1;:10;:55;35141:64;;;;;;35211:15;:36;;-1:-1:-1;;;;;;;;35211:36:1;-1:-1:-1;;;;;;;;35211:36:1;;;;;;;;;;;;35259:29;;;;-1:-1:-1;;35259:29:1;35067:226;:::o;3189:2093::-;3346:25;;;3305:10;3317:7;2104:21;2094:31;;;;;2086:47;;;;;-1:-1:-1;;;2086:47:1;;;;;;;;;;;;-1:-1:-1;;;2086:47:1;;;;;;;;;;;;;;;2157:10;2147:20;;:7;:20;;;2139:36;;;;;-1:-1:-1;;;2139:36:1;;;;;;;;;;;;-1:-1:-1;;;2139:36:1;;;;;;;;;;;;;;;-1:-1:-1;;2189:34:1;;;;;2181:50;;;;;-1:-1:-1;;;2181:50:1;;;;;;;;;;;;-1:-1:-1;;;2181:50:1;;;;;;;;;;;;;;;3463:24:::1;;:::i;:::-;3535:17;::::0;;::::1;::::0;::::1;3501:31;3535:17:::0;;;:5:::1;:17;::::0;;;;;;;;3651:26:::1;::::0;::::1;::::0;-1:-1:-1;;;3728:24:1;::::1;;;3614:23:::0;;::::1;3560:200:::0;;;;-1:-1:-1;;;3687:31:1;::::1;-1:-1:-1::0;;;;;3687:31:1::1;3583:29:::0;;::::1;3560:200:::0;;;;3651:26:::1;::::0;;::::1;3560:200:::0;::::1;::::0;::::1;::::0;;3535:17;-1:-1:-1;;;3776:18:1;::::1;;;3768:27;;;::::0;::::1;;2237:1;3808:24;;:::i;:::-;3880:14;::::0;;::::1;::::0;::::1;3846:31;3880:14:::0;;;:5:::1;:14;::::0;;;;;;;;3993:26:::1;::::0;::::1;::::0;-1:-1:-1;;;4070:24:1;::::1;;;3956:23:::0;;::::1;3902:200:::0;;;;-1:-1:-1;;;4029:31:1;::::1;-1:-1:-1::0;;;;;4029:31:1::1;3925:29:::0;;::::1;3902:200:::0;;;;3993:26:::1;::::0;;::::1;3902:200:::0;::::1;::::0;::::1;::::0;;3880:14;-1:-1:-1;;;4119:18:1;::::1;;;4111:27;;;::::0;::::1;;-1:-1:-1::0;4204:11:1::1;:16:::0;;-1:-1:-1;;;4204:16:1;::::1;::::0;::::1;::::0;-1:-1:-1;;;4222:26:1;;::::1;;;::::0;4260:24;;::::1;::::0;;;::::1;;4256:243;;;4334:5;:20;;;4311:5;:20;;;:43;4396:5;:29;;;4364:5;:29;;;:61;4461:5;:23;;;4435:5;:23;;;:49;4294:198;;;;;;;;;;;;4256:243;4523:7;4509:21;;:11;:21;;;4505:576;;;4540:17;4560;:15;:17::i;:::-;4777:9;::::0;4540:37;;-1:-1:-1;4586:26:1::1;::::0;;;4665:129:::1;::::0;4540:37;;4586:26;;4725:11;;4746:21;;-1:-1:-1;;;;;4777:9:1::1;4665:19;:129::i;:::-;4585:209;;;;;;4865:5;:20;;;4842:5;:20;;;4819;:43;:66;4965:5;:29;;;4933:5;:29;;;4895:35;:67;:99;5043:5;:23;;;5017:5;:23;;;5004:10;:36;:62;4802:272;;;;;;;;;;;;;;;4505:576;5125:5;:20;;;5102:5;:20;;;:43;5185:5;:29;;;5153:5;:29;;;:61;5248:5;:23;;;5222:5;:23;;;:49;5087:190;;;;;;;;;;2237:1;3189:2093:::0;;;;;;;:::o;5329:512::-;5438:30;;;;-1:-1:-1;;;;;5665:19:1;5644:55;;5709:17;:15;:17::i;:::-;5757:11;:16;;5819:9;;5644:192;;;;;;-1:-1:-1;;;;;;5644:192:1;;;;;;;;;;;;-1:-1:-1;;;5757:16:1;;;;5644:192;;;;;;;;-1:-1:-1;;;5783:26:1;;;;;5644:192;;;;;;-1:-1:-1;;;;;5819:9:1;;;5644:192;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;5736:11;;5644:192;;5757:16;;5783:26;5644:192;;;;;5736:11;-1:-1:-1;5644:192:1;;;5736:11;5644:192;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5631:205;;;;;;;;5329:512;;;;;;;:::o;17234:692::-;1570:11:5;:20;17381:15:1;;;;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;17351:10:1;;17363:7;;2104:21;2094:31;;;::::1;;2086:47;;;::::0;;-1:-1:-1;;;2086:47:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2086:47:1;;;;;;;;;;;;;::::1;;2157:10;2147:20;;:7;:20;;;2139:36;;;::::0;;-1:-1:-1;;;2139:36:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2139:36:1;;;;;;;;;;;;;::::1;;-1:-1:-1::0;;2189:34:1::1;::::0;;::::1;;2181:50;;;::::0;;-1:-1:-1;;;2181:50:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2181:50:1;;;;;;;;;;;;;::::1;;17422:25:::2;::::0;;17489:118:::2;17524:10;17542::::0;17560:7;17576:25:::2;-1:-1:-1::0;;;;;17576:14:1;::::2;:23;:25::i;:::-;17575:26;;17489:27;:118::i;:::-;17421:186;;;;;;17633:10;17632:11;;17614:30;;17669:10;17668:11;;17650:30;;17701:7;17691;:17;17712:1;17691:22;17687:160;;17759:14;::::0;::::2;::::0;:39:::2;::::0;-1:-1:-1;;;;;17759:14:1::2;17789:7:::0;17759:21:::2;:39::i;:::-;17800:14;::::0;::::2;::::0;:39:::2;::::0;-1:-1:-1;;;17800:14:1;::::2;-1:-1:-1::0;;;;;17800:14:1::2;17830:7:::0;17800:21:::2;:39::i;:::-;17724:14;::::0;::::2;17723:117:::0;;-1:-1:-1;;;;;17723:117:1;;::::2;-1:-1:-1::0;;;17723:117:1;;::::2;::::0;;;::::2;::::0;::::2;::::0;;;::::2;-1:-1:-1::0;;;;;;17723:117:1::2;;::::0;;17687:160:::2;17858:63;::::0;;-1:-1:-1;;;;;17858:63:1;::::2;::::0;;::::2;::::0;::::2;::::0;;;;;;;;;;;::::2;::::0;;::::2;::::0;;;;::::2;::::0;17863:10:::2;::::0;17858:63:::2;::::0;;;;;;;;::::2;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;-1:-1:-1;17234:692:1;;;;-1:-1:-1;17234:692:1;;-1:-1:-1;;;;17234:692:1:o;13937:2278::-;1570:11:5;:20;14189:15:1;;;;;;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;14148:10:1;;14160:7;;2104:21;2094:31;;;::::1;;2086:47;;;::::0;;-1:-1:-1;;;2086:47:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2086:47:1;;;;;;;;;;;;;::::1;;2157:10;2147:20;;:7;:20;;;2139:36;;;::::0;;-1:-1:-1;;;2139:36:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2139:36:1;;;;;;;;;;;;;::::1;;-1:-1:-1::0;;2189:34:1::1;::::0;;::::1;;2181:50;;;::::0;;-1:-1:-1;;;2181:50:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2181:50:1;;;;;;;;;;;;;::::1;;-1:-1:-1::0;;;;;14281:20:1;::::2;14273:35;;;::::0;;-1:-1:-1;;;14273:35:1;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;14273:35:1;;;;;;;;;;;;;::::2;;14323:17;::::0;14365:166:::2;14398:10:::0;14418:7;14435:35:::2;-1:-1:-1::0;;;;;14435:24:1;::::2;:33;:35::i;:::-;14480:11;:16:::0;;-1:-1:-1;;;14480:16:1;::::2;::::0;;::::2;::::0;-1:-1:-1;;;;;14506:17:1::2;14365:23;:166::i;:::-;-1:-1:-1::0;14322:209:1;;-1:-1:-1;14322:209:1;-1:-1:-1;14619:23:1::2;::::0;-1:-1:-1;14619:23:1;;-1:-1:-1;14689:11:1;;14685:50:::2;;14720:15;:13;:15::i;:::-;14702:33;;14685:50;14747:11:::0;;14743:50:::2;;14778:15;:13;:15::i;:::-;14760:33;;14743:50;14801:76;::::0;-1:-1:-1;;;14801:76:1;;::::2;::::0;::::2;::::0;;;;;;;;;;;;;;;;;;;;;;14822:10:::2;::::0;14801:52:::2;::::0;14854:7;;14863;;14872:4;;;;14801:76;;;;14872:4;;;;14801:76;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;14899:1;14889:7;:11;14885:91;;;14966:1;14947:15;14929;:13;:15::i;:::-;:33;14911:51;;;14910:57;14902:74;;;::::0;;-1:-1:-1;;;14902:74:1;;::::2;;::::0;;::::2;::::0;;;;;;;;-1:-1:-1;;;14902:74:1;;;;;;;;;;;;;::::2;;14988:11:::0;;14984:91:::2;;15065:1;15046:15;15028;:13;:15::i;:::-;:33;15010:51;;;15009:57;15001:74;;;::::0;;-1:-1:-1;;;15001:74:1;;::::2;;::::0;;::::2;::::0;;;;;;;;-1:-1:-1;;;15001:74:1;;;;;;;;;;;;;::::2;;15106:16;15088:34;;15150:7;15132:15;:25;15128:140;;;15193:67;-1:-1:-1::0;;;;;15209:24:1;::::2;15235:15:::0;15252:7;15193:15:::2;:67::i;:::-;15167:94;;15128:140;15295:7;15277:15;:25;15273:246;;;15312:23;15346:67;-1:-1:-1::0;;;;;15362:24:1;::::2;15388:15:::0;15405:7;15346:15:::2;:67::i;:::-;15312:102:::0;-1:-1:-1;;;;;;15426:33:1;;::::2;::::0;;::::2;;15422:91;;;15489:15;15471:33;;15422:91;15273:246;;-1:-1:-1::0;;;;;15533:19:1;::::2;15525:36;;;::::0;;-1:-1:-1;;;15525:36:1;;::::2;;::::0;;::::2;::::0;;;;;;;;-1:-1:-1;;;15525:36:1;;;;;;;;;;;;;::::2;;15579:17;::::0;15619:95:::2;15647:9:::0;15658:10;15670:7;15679:34:::2;-1:-1:-1::0;;;;;15679:23:1;::::2;:32;:34::i;:::-;15619:27;:95::i;:::-;15576:138;;;;;15766:15;15750:10;15732:29;;;15731:50;;15723:68;;;::::0;;-1:-1:-1;;;15723:68:1;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;15723:68:1;;;;;;;;;;;;;::::2;;15842:15;15826:10;15808:29;;;15807:50;;15799:68;;;::::0;;-1:-1:-1;;;15799:68:1;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;15799:68:1;;;;;;;;;;;;;::::2;;2237:1;;15902:7;15884:15;:25;15880:116;;;15919:70;15947:6;15955;15981:7;15963:15;:25;15919:27;:70::i;:::-;16023:7;16005:15;:25;16001:116;;;16040:70;16068:6;16076;16102:7;16084:15;:25;16040:27;:70::i;:::-;16167:7;16127:83;;16155:10;16127:83;;16144:9;-1:-1:-1::0;;;;;16127:83:1::2;;16132:10;16176:15;16193:7;16202;16127:83;;;;-1:-1:-1::0;;;;;16127:83:1::2;;;;;;-1:-1:-1::0;;;;;16127:83:1::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;-1:-1:-1;13937:2278:1;;;;-1:-1:-1;13937:2278:1;-1:-1:-1;13937:2278:1;-1:-1:-1;;;;;;13937:2278:1:o;408:41:4:-;;;:::o;1368:51:5:-;;;;;;;;;;;;;:::o;665:102:4:-;401:2:22;665:102:4;:::o;579:40::-;;;:::o;912:39:5:-;;;;-1:-1:-1;;;;;912:39:5;;;-1:-1:-1;;;912:39:5;;;;;;-1:-1:-1;;;912:39:5;;;;;-1:-1:-1;;;912:39:5;;;;;;;-1:-1:-1;;;912:39:5;;;;;-1:-1:-1;;;912:39:5;;;;;-1:-1:-1;;;912:39:5;;;;;:::o;828:44::-;;;;:::o;1272:56::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1272:56:5;;;-1:-1:-1;;;1272:56:5;;;;;;;;;;;;-1:-1:-1;;;1272:56:5;;-1:-1:-1;;;;;1272:56:5;;-1:-1:-1;;;1272:56:5;;;;;-1:-1:-1;;;1272:56:5;;;;;:::o;5883:494:1:-;5957:11;:17;-1:-1:-1;;;;;5957:17:1;:22;5949:37;;;;;-1:-1:-1;;;5949:37:1;;;;;;;;;;;;-1:-1:-1;;;5949:37:1;;;;;;;;;;;;;;;6057:10;6070:41;6098:12;6070:27;:41::i;:::-;6057:54;;6118:16;6137:17;:15;:17::i;:::-;6160:69;;;-1:-1:-1;;;6160:69:1;;;;;;;;;;;;;;;;;;;6118:36;;-1:-1:-1;;;;;;6181:19:1;6160:52;;;;:69;;;;;-1:-1:-1;;6160:69:1;;;;;;;;-1:-1:-1;6160:52:1;:69;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;6236:11:1;:32;;6307:23;;;;;;-1:-1:-1;;;6307:23:1;-1:-1:-1;;;;;;;;;;;;;6236:32:1;;-1:-1:-1;;;;;;6236:32:1;;;;;6274:27;-1:-1:-1;;;6274:27:1;6307:23;;;;;6342:30;;;;;;;;;;;;;;;;;-1:-1:-1;6342:30:1;;;;;;;;-1:-1:-1;6342:30:1;5883:494;;;:::o;1192:39:5:-;;;-1:-1:-1;;;1192:39:5;;-1:-1:-1;;;;;1192:39:5;;:::o;24965:7653:1:-;25107:14;25129;25151:20;25179:17;25204:24;25236:26;25277:21;25304:33;;:::i;:::-;25402:11;:17;;25477:15;-1:-1:-1;;;25477:15:1;;;;25465:9;;;:27;-1:-1:-1;;;25523:26:1;;;25500:20;;;:49;-1:-1:-1;;;;25738:28:1;;;;-1:-1:-1;;;;;25402:17:1;;;-1:-1:-1;;;;25441:16:1;;;;;;-1:-1:-1;;;;;25587:30:1;;;;;-1:-1:-1;;;25655:30:1;;;;;-1:-1:-1;;;25709:20:1;;;;;25820:24;;;;;-1:-1:-1;;;25820:24:1;;;;;;;;;;;;-1:-1:-1;;;25820:24:1;;;;;;;;;;;;;;;25861:19;25853:34;;;;;-1:-1:-1;;;25853:34:1;;;;;;;;;;;;-1:-1:-1;;;25853:34:1;;;;;;;;;;;;;;;25979:1;25962:18;;25925:16;;;25895:86;25896:27;;;25895:86;;;26045:9;;-1:-1:-1;;;;;;;;26056:25:1;;;;26009:31;;;25990:92;26045:9;;-1:-1:-1;26091:443:1;;;;-1:-1:-1;;;;;26124:29:1;;;;;;;:73;;;;-1:-1:-1;938:10:31;-1:-1:-1;;;;;26157:40:1;;;26124:73;26116:89;;;;;-1:-1:-1;;;26116:89:1;;;;;;;;;;;;-1:-1:-1;;;26116:89:1;;;;;;;;;;;;;;;26238:20;;26215;;;:43;26268:40;;;26091:443;;;-1:-1:-1;;;;;26341:29:1;;;;;;;:73;;;;-1:-1:-1;1114:49:31;-1:-1:-1;;;;;26374:40:1;;;26341:73;26333:89;;;;;-1:-1:-1;;;26333:89:1;;;;;;;;;;;;-1:-1:-1;;;26333:89:1;;;;;;;;;;;;;;;26455:20;;26432;;;:43;26485:40;;;26091:443;26542:29;;;;;;:15;;;:29;26597:17;:15;:17::i;:::-;26627:15;;26580:34;;-1:-1:-1;;;;26627:15:1;;-1:-1:-1;;;;;26627:15:1;:29;26623:557;;26725:15;;;26705:71;;;-1:-1:-1;;;26705:71:1;;;;;;;;;;;;;-1:-1:-1;;;;;26725:15:1;;;-1:-1:-1;;;;;26725:15:1;;26705:55;;:71;;;;;;;;;;;;;;-1:-1:-1;26725:15:1;26705:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;26705:71:1;;-1:-1:-1;26801:36:1;26790:7;:47;;;;;;;;;26786:386;;;26851:15;:28;;-1:-1:-1;;;;;;;;26851:28:1;;;26786:386;;;26911:33;26900:7;:44;;;;;;;;;26896:276;;;26982:33;26958:21;;;:57;26896:276;;;27047:38;27036:7;:49;;;;;;;;;27032:140;;;27123:38;27099:21;;;:62;27032:140;26623:557;;27188:24;27215:169;27240:5;:20;;;27270:14;27294:5;:15;;;27319:16;27345:5;:31;;;27215:15;:169::i;:::-;27188:196;;27478:5;:20;;;27457:41;;:17;:41;;;27453:249;;27510:40;;;:20;;;:40;27594:1;27560:31;;;:35;27617:76;27628:14;27644:11;27533:17;27676:16;27617:10;:76::i;:::-;27605:88;;:9;;;:88;27453:249;24965:7653;;;;27714:30;;:::i;:::-;-1:-1:-1;;;;;27859:33:1;;;;27937:54;:9;27968:11;27981:9;27937:30;:54::i;:::-;27901:90;;27917:16;;;27901:90;;;;;;;27902:13;;;27901:90;;;28021:42;;:27;:42::i;:::-;-1:-1:-1;;;;;28000:63:1;;;:18;;;:63;;;28246:326;;28296:9;;28315:12;;28352:35;;-1:-1:-1;28338:50:1;;;;28337:158;;28477:4;:18;;;28337:158;;;28450:14;28337:158;28505:16;28531:14;28555:5;:9;;;28246:40;:326::i;:::-;28228:14;;;28188:384;28215:11;;;28188:384;28203:10;;;28188:384;28585:16;;;;28188:384;;-1:-1:-1;28581:517:1;;;28631:40;28645:4;:14;;;28632:4;:10;;;:27;28631:38;:40::i;:::-;28613:58;;;;28741:50;28768:22;:4;:11;;;:20;:22::i;:::-;28741;;;;;:26;:50::i;:::-;28716:22;;;:75;28581:517;;;28871:22;:4;:11;;;:20;:22::i;:::-;28853:40;;;;28985:68;29012:40;29026:4;:14;;;29013:4;:10;;;:27;29012:38;:40::i;:::-;28985:22;;;;;:26;:68::i;:::-;28960:22;;;:93;28581:517;29110:18;;:22;29106:211;;29180:18;;29161:14;;;;29144:13;;725:4:22;;29161:38:1;;:18;:38::i;:::-;29160:78;;;;;29248:14;;;:23;;29160:78;;;;29248:23;;;;;;29281:27;;;;;-1:-1:-1;29106:211:1;-1:-1:-1;;;;;29329:20:1;;;29325:115;;29391:14;;;;29375:65;;-1:-1:-1;;;;;;;;29375:65:1;;:15;:65::i;:::-;29351:20;;;:89;;;;;;;29325:115;29469:18;;;;-1:-1:-1;;;;;29453:34:1;;;;;;29449:2137;;;29574:4;:16;;;29570:1664;;;29690:5;:29;;;29685:521;;29801:249;29836:14;29866:1;29883:5;:15;;;29914:5;:20;;;29950:16;29801:19;:249::i;:::-;-1:-1:-1;;;;;;;29735:315:1;29758:35;;;29735:315;;;;;;;29736:20;;;29735:315;-1:-1:-1;30064:29:1;;;:36;30138:9;:55;;30173:20;;30138:55;;;30150:20;;30138:55;30114:21;;;:79;29685:521;30321:36;30296:5;:21;;;:61;;;;;;;;;30292:162;;30393:15;;;30416:13;;;;30373:68;;;-1:-1:-1;;;30373:68:1;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;30393:15:1;;;-1:-1:-1;;;;;30393:15:1;;30373:42;;:68;;;;;-1:-1:-1;;30373:68:1;;;;;;;-1:-1:-1;30393:15:1;30373:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30292:162;30465:21;30502:9;30498:638;;;30545:264;30572:4;:13;;;30601:5;:20;;;30647:5;:21;;;30694:5;:35;;;30745:5;:20;;;30781:14;30545:5;:11;;:264;;;;;;;;;:::i;:::-;30544:265;;30527:282;;30498:638;;;30859:264;30886:4;:13;;;30915:5;:21;;;30962:5;:20;;;31008:5;:35;;;31059:5;:20;;;31095:14;30859:5;:11;;:264;;;;;;;;;:::i;:::-;30842:281;;30498:638;31167:56;31190:16;31208:14;31167:22;:56::i;:::-;31148:75;;29570:1664;;31258:9;:45;;31290:4;:13;;;31258:45;;;31286:1;31270:4;:13;;;:17;31258:45;31244:59;;29449:2137;;;31338:18;;-1:-1:-1;;;;;31322:34:1;;;;;;31318:268;;31448:41;31476:12;31448:27;:41::i;:::-;31434:55;;31499:5;;31318:268;31628:19;;;:53;;-1:-1:-1;;;;;;31651:30:1;;;;;;;31628:53;31624:83;;;31693:5;;31624:83;27838:3875;;;31753:5;:16;;;31740:29;;:9;:29;;;:353;;32024:5;:22;;;32078:14;32048:5;:27;;;:44;31740:353;;;31894:14;31864:5;:27;;;:44;31910:5;:22;;;31740:353;32213:9;;;;32224:20;;;;32101:11;32100:145;;-1:-1:-1;;;;32100:145:1;-1:-1:-1;;;;32100:145:1;;;;;-1:-1:-1;;;;32100:145:1;-1:-1:-1;;;32100:145:1;;;;;;;;;;;;-1:-1:-1;;;;32100:145:1;-1:-1:-1;;;;32100:145:1;;;;;;-1:-1:-1;;;;;;32100:145:1;-1:-1:-1;;;;;32100:145:1;;;;;;;;;;32359:105;;;-1:-1:-1;;;32359:105:1;;-1:-1:-1;;;;;32359:105:1;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32359:105:1;;-1:-1:-1;32359:105:1;;32380:19;32359:69;;;;;;:105;;;;;;;;;;;;;;:69;:105;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32359:105:1;;32325:31;;;32253:9;32252:218;;-1:-1:-1;;;;;32252:218:1;;;-1:-1:-1;;;32325:139:1;;;;32252:218;;;;;;;;;;;;;;-1:-1:-1;;;;;;32252:218:1;;;;32477:137;;;;32523:20;;;;32500;:43;32477:137;;;32587:20;;;;32564;:43;32477:137;24965:7653;;;;;;;;;;;;;:::o;723:292:34:-;865:66;;;-1:-1:-1;;;;;865:66:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;865:66:34;-1:-1:-1;;;865:66:34;;;854:78;;;;-1:-1:-1;;;;854:10:34;;;;865:66;854:78;;;865:66;854:78;;865:66;854:78;;;;;;;;;;-1:-1:-1;;854:78:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;818:114;;;;946:7;:57;;;;-1:-1:-1;958:11:34;;:16;;:44;;;989:4;978:24;;;;;;;;;;;;;;;-1:-1:-1;978:24:34;958:44;938:72;;;;;-1:-1:-1;;;938:72:34;;;;;;;;;;;;-1:-1:-1;;;938:72:34;;;;;;;;;;;;;;;723:292;;;;;:::o;2339:120:1:-;2408:46;;;-1:-1:-1;;;2408:46:1;;2448:4;2408:46;;;;;;2386:7;;-1:-1:-1;;;;;2422:6:1;2408:31;;;;:46;;;;;;;;;;;;;;;:31;:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2408:46:1;;-1:-1:-1;2339:120:1;:::o;19292:187::-;19398:76;;-1:-1:-1;;;19398:76:1;;;;;;;;;;;;;;;;;;;;;;;;;;;19419:10;;19398:52;;19451:7;;19460;;19469:4;;;;19398:76;;;;19469:4;;;;19398:76;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19292:187;;;;:::o;558:105:26:-;646:5;;;641:16;;;;633:25;;;;;;558:105;;;;:::o;2463:120:1:-;2532:46;;;-1:-1:-1;;;2532:46:1;;2572:4;2532:46;;;;;;2510:7;;-1:-1:-1;;;;;2546:6:1;2532:31;;;;:46;;;;;;;;;;;;;;;:31;:46;;;;;;;;;;18253:186;18324:13;18356:7;-1:-1:-1;;;;;18340:37:1;;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;18340:39:1;;-1:-1:-1;18385:49:1;18413:5;18340:39;18427:6;18385:27;:49::i;:::-;18253:186;;;:::o;4356:491:24:-;4464:14;4490:6;;;:37;;-1:-1:-1;;4511:5:24;;;4525:1;4520;4511:5;4520:1;4501:20;;;;;:25;4490:37;4486:357;;;4559:1;4545:11;:15;4537:24;;;;;;4602;;;4631;;;;4628:31;;4598:62;4578:90;;;4697:25;4704:1;4707;4710:11;4697:6;:25::i;:::-;4688:34;;4762:1;4747:11;4734:25;;;;;4744:1;4741;4734:25;:29;4730:107;;;-1:-1:-1;;4783:26:24;;4775:35;;;;;;4820:8;;4730:107;4356:491;;;;;:::o;757:3329::-;855:14;1176:5;;;855:14;-1:-1:-1;;1176:5:24;;1322:20;1385:5;1381:2;1378:13;1370:5;1366:2;1362:14;1358:34;1349:43;;;1513:5;1499:11;:19;1491:28;;;;;;1584:10;1580:111;;-1:-1:-1;1633:23:24;;;;-1:-1:-1;1671:13:24;;1580:111;2044:11;2041:1;2038;2031:25;2280:12;2295;;;:26;;2401:22;;;;;3128:1;3109;:15;;3108:21;;3339:17;;;3335:21;;3328:28;3393:17;;;3389:21;;3382:28;3448:17;;;3444:21;;3437:28;3503:17;;;3499:21;;3492:28;3558:17;;;3554:21;;3547:28;3614:17;;;3610:21;;;3603:28;2729:12;;;;2725:23;;;2750:1;2721:31;2083:20;;;2072:32;;;2772:12;;;;2120:21;;;;2511:16;;;;2763:21;4051:11;;-1:-1:-1;757:3329:24;;;;;:::o;13596:299:1:-;13848:8;13835:22;;;13806:25;;13794:2;13790:14;;;13787:45;13779:54;;;13776:82;13711:16;13876:14;;;:9;:14;;;;;;13596:299::o;817:105:26:-;905:5;;;900:16;;;;892:25;;;;;1632:114;1717:5;;;1712:16;;;;1688:8;1733:6;;;1712:28;1704:37;;;;;1793:132:5;1879:15;1793:132;:::o;18792:496:1:-;19152:131;;;-1:-1:-1;;;19152:131:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;19152:131:1;;;;;;;;-1:-1:-1;;;;;;;;19173:19:1;-1:-1:-1;;;;;19152:60:1;;-1:-1:-1;;19152:131:1;;;;;-1:-1:-1;;19152:131:1;;;;;;;:60;:131;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;19152:131:1;;;;;;;;;;;;;;;;;;;-1:-1:-1;19152:131:1;;-1:-1:-1;19152:131:1;-1:-1:-1;18792:496:1;-1:-1:-1;;;;;;18792:496:1:o;763:101:28:-;857:1;838:20;;;;;;830:29;;;;;;763:101;;;:::o;9237:2953:1:-;9516:84;;;;;;;;9536:11;:17;;-1:-1:-1;;;;;9536:17:1;;9516:84;;-1:-1:-1;;;9555:16:1;;;;9516:84;;;;;;;-1:-1:-1;;;9573:26:1;;;;9516:84;;;;;;;-1:-1:-1;;;;;;9618:47:1;9638:5;9645:10;9657:7;9618:19;:47::i;:::-;9673:29;9738:20;;9760;;9607:58;;-1:-1:-1;9738:20:1;9760;9673:29;9837:19;;;;;9833:974;;9866:11;9880:17;:15;:17::i;:::-;10002:10;;;;10014:20;;;;10036:9;;9866:31;;-1:-1:-1;9906:20:1;;;;9973:73;;9866:31;;9906:20;;10002:10;10014:20;-1:-1:-1;;;;;10036:9:1;9973:19;:73::i;:::-;9905:141;;;;;;10068:271;10092:10;10114:5;:10;;;10136:14;10162:21;10195;10228:29;10269:14;10295:4;10311:5;10068;:12;;:271;;;;;;;;;;;;:::i;:::-;10055:374;;;10374:4;;-1:-1:-1;10388:32:1;:9;10409:10;10388:20;:32::i;:::-;10493:10;;;;10450:267;;:5;;10474:7;;10515:14;10541:21;10574;10607:29;10648:14;10674:4;10690;10450:12;:267::i;:::-;10437:364;;;10749:4;;-1:-1:-1;10763:29:1;:9;10784:7;10763:20;:29::i;:::-;9833:974;;;;10940:10;;;;10814:28;;;;10876:138;;:5;;10907:10;;10925:7;;10958:21;10987;10876:23;:138::i;:::-;10813:201;;;;11021:90;11042:8;11052:14;11068:20;11090;11021;:90::i;:::-;11122:14;:19;;11140:1;11122:19;11118:1068;;11305:1;11288:14;:18;;;11284:133;;;11322:13;11318:43;;;11344:17;;;;;;;;;;:5;:17;;;;;11337:24;;;;;;;;;;;;;;;;;;;;11318:43;11375:10;11371:37;;;11394:14;;;;;;;;;;:5;:14;;;;;11387:21;;;;;;;;;;;;;;;;;;;;11371:37;11425:27;11503:85;11527:10;11539:7;11548:14;11564:5;:10;;;11576:5;:11;;;11503:23;:85::i;:::-;11460:128;;-1:-1:-1;11460:128:1;-1:-1:-1;11460:128:1;-1:-1:-1;11600:25:1;;;;;11596:584;;11663:9;;11725:20;;;;-1:-1:-1;;;;;11663:9:1;;;;11637:23;;11709:112;;11747:17;:15;:17::i;:::-;11766:10;;;;11795:25;;11778:15;;-1:-1:-1;;;11795:25:1;;-1:-1:-1;;;;;11795:25:1;11709:15;:112::i;:::-;11682:139;;11859:17;11835:41;;:5;:20;;;:41;;;11831:264;;11908:77;11919:17;:15;:17::i;:::-;11938:5;:10;;;11950:17;11969:15;11908:10;:77::i;:::-;11890:11;:95;;-1:-1:-1;;;;11890:95:1;-1:-1:-1;;;;11890:95:1;;;;;-1:-1:-1;;;;11997:46:1;-1:-1:-1;;;11997:46:1;;;;;;;;;;;;;12055:25;:29;;-1:-1:-1;;;;;12055:29:1;;;11831:264;12116:55;12139:15;12156:14;12116:22;:55::i;:::-;12104:9;:67;;-1:-1:-1;;;;;;12104:67:1;-1:-1:-1;;;;;12104:67:1;;;;;;;;;;-1:-1:-1;;11596:584:1;11118:1068;;9237:2953;;;;;;;;;;;;;;;:::o;1901:108:26:-;1992:5;;;-1:-1:-1;;;;;1987:16:26;;;;;;;;1979:25;;;;;12194:1142:1;12389:14;12411;12433:27;12593:10;12579:24;;:11;:24;;;12575:757;;;12623:124;12653:39;12681:10;12653:27;:39::i;:::-;12694:36;12722:7;12694:27;:36::i;:::-;12732:14;12623:29;:124::i;:::-;12613:134;;12575:757;;;12778:7;12764:21;;:11;:21;;;12760:572;;;12805:97;12835:12;12849:36;12877:7;12849:27;:36::i;12805:97::-;12795:107;;12920:100;12950:39;12978:10;12950:27;:39::i;:::-;12991:12;13005:14;12920:29;:100::i;:::-;12910:110;;13052:14;13029:37;;12760:572;;;13201:124;13231:39;13259:10;13231:27;:39::i;:::-;13272:36;13300:7;13272:27;:36::i;:::-;13310:14;13201:29;:124::i;:::-;13191:134;;12760:572;12194:1142;;;;;;;;;:::o;4330:3552:31:-;4396:10;938;-1:-1:-1;;;;;4517:23:31;;;;;;:49;;-1:-1:-1;1114:49:31;-1:-1:-1;;;;;4544:22:31;;;4517:49;4509:63;;;;;-1:-1:-1;;;4509:63:31;;;;;;;;;;;;-1:-1:-1;;;4509:63:31;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;4594:20:31;;;;-1:-1:-1;;;;;4699:41:31;;4696:1;4692:49;4777:9;;;-1:-1:-1;;;;;4830:25:31;;4827:1;4823:33;4892:9;;;4951:10;4945:17;;4942:1;4938:25;4999:9;;;5058:6;5052:13;;5049:1;5045:21;5102:9;;;5161:4;5155:11;;5152:1;5148:19;;;5203:9;;;5262:3;5256:10;;5253:1;5249:18;5303:9;;;5356:10;;;-1:-1:-1;5349:18:31;;;5403:9;;;;5449:10;;;4870;;4977;;;5080;;;5181;5281;;;5381;5473;-1:-1:-1;5499:10:31;;5495:75;;5531:3;5525;:9;5515:5;:20;;5511:24;;5495:75;;;5566:3;5560;:9;5550:5;:20;;5546:24;;5495:75;5655:9;;;5650:3;5646:19;;;5681:11;;;;5741:9;;;;5792;;5783:19;;;5818:11;;;5878:9;5929;;5920:19;;;5955:11;;;6015:9;6066;;6057:19;;;6092:11;;;6152:9;6203;;6194:19;;;6229:11;;;6289:9;6340;;6331:19;;;6366:11;;;6426:9;6477;;6468:19;;;6503:11;;;6563:9;6614;;6605:19;;;6640:11;;;6700:9;6751;;6742:19;;;6777:11;;;6837:9;6888;;6879:19;;;6914:11;;;6974:9;7025;;7016:19;;;7051:11;;;7111:9;7162;;7153:19;;;7188:11;;;7248:9;7299;;7290:19;;;7325:11;;;;7385:9;;;;7436;;7427:19;;;;;5655:9;-1:-1:-1;;5593:17:31;;5615:2;5592:25;5718:10;;;;;-1:-1:-1;;;5718:10:31;5708:21;5855:10;;;;;-1:-1:-1;;;5855:10:31;5845:21;;;;5992:10;;;;;-1:-1:-1;;;5992:10:31;5982:21;;;;6129:10;;;;;-1:-1:-1;;;6129:10:31;6119:21;;;;6266:10;;;;;-1:-1:-1;;;6266:10:31;6256:21;;;;6403:10;;;;;-1:-1:-1;;;6403:10:31;6393:21;;;;6540:10;;;;;-1:-1:-1;;;6540:10:31;6530:21;6677:10;;;;;-1:-1:-1;;;6677:10:31;6667:21;6814:10;;;;;-1:-1:-1;;;6814:10:31;6804:21;6951:10;;;;;-1:-1:-1;;;6951:10:31;6941:21;7088:10;;;;;-1:-1:-1;;;7088:10:31;7078:21;7225:10;;;;;-1:-1:-1;;;7225:10:31;7215:21;7362:10;;;;;-1:-1:-1;;;7362:10:31;7352:21;7499:10;;;;;-1:-1:-1;;;7499:10:31;7489:21;7553:24;7545:32;;-1:-1:-1;;7625:53:31;;5607:3;7624:62;;;;7731:39;7715:55;;7714:64;;7793:17;;;;;;;;;:84;;-1:-1:-1;;;;;7823:35:31;;:26;7842:6;7823:18;:26::i;:::-;-1:-1:-1;;;;;7823:35:31;;;:54;;7870:7;7823:54;;;7861:6;7823:54;7793:84;;;7813:7;7793:84;7786:91;4330:3552;-1:-1:-1;;;;;;;;;4330:3552:31:o;18443:345:1:-;18660:123;;;-1:-1:-1;;;18660:123:1;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;18660:123:1;;;;;;;;;;;;;;;-1:-1:-1;;18681:19:1;-1:-1:-1;;;;;18660:47:1;;;;:123;;;;;;;;;;;;;;;-1:-1:-1;18660:47:1;:123;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;18660:123:1;;-1:-1:-1;18443:345:1;;;;;;;;:::o;17991:258::-;18140:82;;;-1:-1:-1;;;18140:82:1;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;18140:82:1;;;;;;;;-1:-1:-1;;18161:19:1;-1:-1:-1;;;;;18140:48:1;;-1:-1:-1;;18140:82:1;;;;;;;;;;;;;;;:48;:82;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;18140:82:1;18233:11;;;;;;;;;;18140:82;;-1:-1:-1;18233:11:1;;;;;;18140:82;18233:11;;;17991:258;;;;;;:::o;3747:1692:32:-;3878:14;3894:16;3926:17;401:2:22;3926:42:32;;4138:11;4132:4;4127:23;4120:31;4116:36;4112:1;4106:4;4102:12;4098:55;4084:11;4078:4;4073:23;4069:85;4061:93;;4051:111;4178:3;4174:1261;;;4397:1;4393:12;;;4435:15;;;;;;4278;4435;;;;;;;;;;;4366:4;4356:15;;;;4455;;;4435:36;;;;4542:9;;4538:238;;4583:27;4605:4;4583:21;:27::i;:::-;4577:3;:33;4571:40;;4563:48;;;;4629:28;4652:4;4629:22;:28::i;:::-;4659:4;4621:43;;;;;;;;;4538:238;4703:9;4697:16;;4689:24;;;;4731:28;4754:4;4731:22;:28::i;:::-;4761:5;4723:44;;;;;;;;;4174:1261;4895:1;4887:9;;;5023:1;5019:12;;;5117:15;;;;;;4904;5117;;;;;;;;;;;4887:9;;5019:12;4992:4;4982:15;;;5117:30;;5160:9;;5156:273;;5195:37;5227:4;5220;5219:5;;:12;5195:23;:37::i;:::-;5189:44;;5181:52;;;;5276:28;5299:4;5276:22;:28::i;5156:273::-;5356:9;5350:3;:15;5344:22;;5336:30;;;;5384:28;5407:4;5384:22;:28::i;4174:1261::-;3747:1692;;;;;;:::o;1453:2484:31:-;1516:13;1571:16;;;;1580:6;1571:16;;1620:11;;;1619:20;;;1611:29;;762:9;1654:28;;;1646:42;;;;;-1:-1:-1;;;1646:42:31;;;;;;;;;;;;-1:-1:-1;;;1646:42:31;;;;;;;;;;;;;;;1695:13;1721:3;1711:13;;:93;;-1:-1:-1;;;1711:93:31;;;1732:34;1711:93;-1:-1:-1;;;;;1695:109:31;;-1:-1:-1;1824:3:31;1814:13;;:18;1810:83;;1851:34;1843:42;1890:3;1842:51;1810:83;1913:3;1903:13;;:18;1899:83;;1940:34;1932:42;1979:3;1931:51;1899:83;2002:3;1992:13;;:18;1988:83;;2029:34;2021:42;2068:3;2020:51;1988:83;2091:4;2081:14;;:19;2077:84;;2119:34;2111:42;2158:3;2110:51;2077:84;2181:4;2171:14;;:19;2167:84;;2209:34;2201:42;2248:3;2200:51;2167:84;2271:4;2261:14;;:19;2257:84;;2299:34;2291:42;2338:3;2290:51;2257:84;2361:4;2351:14;;:19;2347:84;;2389:34;2381:42;2428:3;2380:51;2347:84;2451:5;2441:15;;:20;2437:85;;2480:34;2472:42;2519:3;2471:51;2437:85;2542:5;2532:15;;:20;2528:85;;2571:34;2563:42;2610:3;2562:51;2528:85;2633:5;2623:15;;:20;2619:85;;2662:34;2654:42;2701:3;2653:51;2619:85;2724:5;2714:15;;:20;2710:85;;2753:34;2745:42;2792:3;2744:51;2710:85;2815:6;2805:16;;:21;2801:86;;2845:34;2837:42;2884:3;2836:51;2801:86;2907:6;2897:16;;:21;2893:86;;2937:34;2929:42;2976:3;2928:51;2893:86;2999:6;2989:16;;:21;2985:86;;3029:34;3021:42;3068:3;3020:51;2985:86;3091:6;3081:16;;:21;3077:86;;3121:34;3113:42;3160:3;3112:51;3077:86;3183:7;3173:17;;:22;3169:86;;3214:33;3206:41;3252:3;3205:50;3169:86;3275:7;3265:17;;:22;3261:85;;3306:32;3298:40;3343:3;3297:49;3261:85;3366:7;3356:17;;:22;3352:83;;3397:30;3389:38;3432:3;3388:47;3352:83;3455:7;3445:17;;:22;3441:78;;3486:25;3478:33;3516:3;3477:42;3441:78;3537:1;3530:4;:8;;;3526:47;;;3568:5;-1:-1:-1;;3568:5:31;3548:25;;;;;3540:33;;3526:47;3900:17;;;:22;:30;;3929:1;3900:30;;;3925:1;3900:30;3883:48;;3893:2;3884:5;:11;;3883:48;3867:65;;1453:2484;;;;;;:::o;5768:2295:27:-;5984:19;6011:13;6032:14;6054:17;6086:69;6158:9;:47;;6189:16;6158:47;;;6170:16;6158:47;6086:119;;6235:1;6216:15;:20;6212:1847;;6270:31;6304:57;6328:15;6352:3;6346:9;;:3;:9;6304:57;;6357:3;6304:15;:57::i;:::-;6270:91;;6377:48;6388:11;6401:12;6415:9;6377:10;:48;;:::i;:::-;6369:56;;6464:5;6437:23;:32;6433:630;;6495:11;;-1:-1:-1;6528:48:27;6554:5;6528:48;;;;6566:3;:9;;;6528:48;:25;:48::i;:::-;6516:60;;6433:630;;;6615:82;6637:12;6651:9;6662:23;6687:9;6615:21;:82::i;:::-;6601:96;-1:-1:-1;;;;;;6711:26:27;;;;;;;6707:348;;6759:48;6770:11;6783:12;6797:9;6759:10;:48;;:::i;:::-;6751:56;;6950:5;6931:15;6923:32;6911:44;;6707:348;;;6996:48;7022:5;6996:48;;;;7034:3;:9;;;6996:48;:25;:48::i;:::-;6984:60;;6707:348;7080:87;7130:11;7143:12;7157:9;7081;:47;;7112:16;7081:47;;;7093:16;7081:47;7080:87;;:::i;:::-;7071:96;;6212:1847;;;;7188:69;7260:9;:47;;7291:16;7260:47;;;7272:16;7260:47;7188:119;;7325:48;7336:11;7349:12;7363:9;7325:10;:48;;:::i;:::-;7316:57;;7400:15;7399:16;;7381:34;;7455:6;7435:15;7427:34;7423:497;;7477:11;7463:25;;7423:497;;;7525:84;7548:12;7562:9;7581:15;7599:9;7525:22;:84::i;:::-;7511:98;-1:-1:-1;;;;;;7624:26:27;;;;;;;7620:112;;7673:48;7684:11;7697:12;7711:9;7673:10;:48;;:::i;:::-;7664:57;;7620:112;7838:15;7821:6;:33;7817:95;;;7885:15;7868:33;;7817:95;7936:48;7947:11;7960:12;7974:9;7936:10;:48;;:::i;:::-;7928:56;-1:-1:-1;8004:48:27;7928:56;8004:48;;;;8042:3;:9;;;8004:48;:25;:48::i;:::-;7992:60;;6212:1847;;5768:2295;;;;;;;;;;;;:::o;1021:111:28:-;1073:8;-1:-1:-1;;;1097:10:28;;1089:19;;;;;;-1:-1:-1;1125:1:28;1021:111::o;1351:114:26:-;1436:5;;;1431:16;;;;1407:8;1452:6;;;1431:28;1423:37;;;;;1077:119;1135:9;1160:6;;;:30;;-1:-1:-1;;1175:5:26;;;1189:1;1184;1175:5;1184:1;1170:15;;;;;:20;1160:30;1152:39;;;;;6525:744:30;6822:10;;;;;;6773:21;6822:10;;;;;;;;;;;;6871:22;;;;;-1:-1:-1;;;;;;;;6871:22:30;-1:-1:-1;;;6871:22:30;;;;;6864:29;;;6839:54;;;;-1:-1:-1;;;;6839:54:30;;;;;;;6963:29;;;;;6931:61;;;6899:93;;;;-1:-1:-1;;;;;;;;6899:93:30;;;;;;;;7042:24;;;;7025:41;;;6998:68;;;;;-1:-1:-1;;6998:68:30;;;;;;;;7124:25;;;;;7101:48;;;7073:76;;;-1:-1:-1;7206:25:30;;;;7183:48;;;7155:76;;7245:19;-1:-1:-1;;;7245:19:30;;;;;6525:744::o;506:210:25:-;568:9;593:1;589;:5;;;585:127;;;-1:-1:-1;629:2:25;;;;617:15;;-1:-1:-1;;;;;612:25:25;;;;;;;604:40;;;;;-1:-1:-1;;;604:40:25;;;;;;;;;;;;-1:-1:-1;;;604:40:25;;;;;;;;;;;;;;;585:127;;;-1:-1:-1;678:14:25;;;-1:-1:-1;;;;;673:25:25;;;;;;;;665:40;;;;;-1:-1:-1;;;665:40:25;;;;;;;;;;;;-1:-1:-1;;;665:40:25;;;;;;;;;;;;;;4246:1554:30;4601:10;;;;;;4561:12;4601:10;;;;;;;;;;4648:19;;-1:-1:-1;;;4648:19:30;;;;;-1:-1:-1;;;;;4704:19:30;4561:12;4760:60;4704:19;4805:14;4760:22;:60::i;:::-;4730:90;-1:-1:-1;4856:36:30;-1:-1:-1;;;;;4834:58:30;;;4826:73;;;;;-1:-1:-1;;;4826:73:30;;;;;;;;;;;;-1:-1:-1;;;4826:73:30;;;;;;;;;;;;;;;5039:5;:141;;5121:59;:48;;:28;;;;:48;;;:32;:48::i;:::-;:57;:59::i;:::-;5039:141;;;5053:59;:48;;:28;;;;:48;;;:32;:48::i;:59::-;5017:163;;-1:-1:-1;;;;;;;;;;;5017:163:30;;;;;;;;;;-1:-1:-1;;;5017:163:30;;;;;5187:41;;;;;;;;;;;;5246:24;;-1:-1:-1;5281:25:30;;5277:519;;5327:7;5326:8;5316:18;;5460:11;5452:19;;:4;:19;;;5448:311;;5483:25;;;:48;;;5541:25;;;:48;;;5599:29;;;:61;;-1:-1:-1;;;;;;;;5599:61:30;-1:-1:-1;;;;;;;;5599:61:30;;;;-1:-1:-1;;5670:41:30;-1:-1:-1;5670:41:30;;;;;;-1:-1:-1;;;;5721:29:30;-1:-1:-1;;;;5721:29:30;;;;;;5448:311;5766:16;;;:23;;-1:-1:-1;;;;;5766:23:30;-1:-1:-1;;;5766:23:30;;;5277:519;4246:1554;;;;;;;;;;;;;;;;:::o;569:414:32:-;401:2:22;664:29:32;;;;;:34;;;656:65;;;;;-1:-1:-1;;;656:65:32;;;;;;;;;;;;-1:-1:-1;;;656:65:32;;;;;;;;;;;;;;;401:2:22;761:30:32;;;;;;926:1;922:12;;;964:1;945:15;;;;;814;945;;;;;;;;;;;;:33;;897:4;887:15;;;964:14;;;;945:33;;;569:414::o;2123:1060:30:-;2437:16;;;;;;2350:28;2437:16;;;;;;;;;;;2480:13;;;;;;;;;;;2350:28;;;;2437:16;;2504:21;;;;;2500:679;;;2554:10;2539:25;;:11;:25;;;2535:337;;2622:5;:26;;;2599:20;:49;2576:72;;2704:5;:26;;;2681:20;:49;2658:72;;2535:337;;;2778:5;:26;;;2755:49;;2837:5;:26;;;2814:49;;2535:337;2903:26;;;;2961;;;;2879:50;;;2937;;;;2500:679;;;3060:5;:26;;;3031:5;:26;;;:55;3008:78;;3146:5;:26;;;3117:5;:26;;;:55;3094:78;;2500:679;2123:1060;;;;;;;;;;;:::o;6840:1845:1:-;7080:19;;-1:-1:-1;;;;;7080:19:1;;;-1:-1:-1;;;7101:35:1;;;;7148:19;;;;7144:686;;-1:-1:-1;;;;;7185:20:1;;7177:35;;;;;-1:-1:-1;;;7177:35:1;;;;;;;;;;;;-1:-1:-1;;;7177:35:1;;;;;;;;;;;;;;;7144:686;;;7293:1;7276:14;:18;;;7272:226;;;7334:17;;;;7365:22;;7361:129;;7460:18;7409:69;;7430:25;7410:17;:15;:17::i;:::-;:45;7409:69;;;;7401:78;;;;;;7272:226;;7541:21;7565:56;7588:16;7606:14;7565:22;:56::i;:::-;7541:80;-1:-1:-1;7541:80:1;-1:-1:-1;;;;;7723:17:1;;:92;;7814:1;7723:92;;;7761:1;7744:14;:18;;;:66;;7785:25;7744:66;;;7765:17;:15;:17::i;:::-;7629:194;;-1:-1:-1;;;;7629:194:1;;;;;;-1:-1:-1;;;7629:194:1;;-1:-1:-1;;;;;;7629:194:1;-1:-1:-1;;;;;7629:194:1;;;;;;;-1:-1:-1;7144:686:1;7895:30;;;;7963;;;;7863:29;8022:45;;;8018:239;;8077:30;;;:53;;;8154:95;8170:44;;;-1:-1:-1;;;;;8154:95:1;;-1:-1:-1;;;8154:15:1;:95::i;:::-;8138:112;;8018:239;8262:13;8309:21;8285:20;:45;8281:239;;8340:30;;;:53;;;8417:95;8433:44;;;-1:-1:-1;;;;;8417:95:1;;-1:-1:-1;;;8417:15:1;:95::i;:::-;8401:112;;8281:239;8590:13;;;-1:-1:-1;;;;;8590:18:1;;8586:95;;8618:15;;;:24;;-1:-1:-1;;;;;;8618:24:1;;-1:-1:-1;;;;;8618:24:1;;;;;;;;8650;;;-1:-1:-1;;;8650:24:1;;;;;;;;;;;;;;;8586:95;6840:1845;;;;;;;;;;:::o;2670:351:33:-;2791:18;2844:1;2831:9;:14;;;;:185;;2939:77;:66;2954:10;2966;2987:9;2986:10;;2999:5;2939:14;:66::i;:::-;:75;:77::i;:::-;2938:78;;2831:185;;;2854:75;:64;2869:10;2881;2901:9;2913:4;2854:14;:64::i;:75::-;2817:199;2670:351;-1:-1:-1;;;;2670:351:33:o;3364:::-;3485:18;3538:1;3525:9;:14;;;;:185;;3633:77;:66;3648:10;3660;3681:9;3680:10;;3693:5;3633:14;:66::i;3525:185::-;3548:75;:64;3563:10;3575;3595:9;3607:4;3548:14;:64::i;2586:495:32:-;2716:1;2712:12;;;2703:22;;;2753:1;2749:12;;;2740:22;2790:1;2786:12;;;2777:22;2827:1;2823:12;;;2814:22;2864:2;2860:13;;;2851:23;2902:2;2898:13;;;2889:23;2940:2;2936:13;;;2927:23;2978:3;2974:14;;;2965:24;3014:12;;;;3004:23;;2654:16;3046:29;3004:23;3046;:29::i;5443:316::-;401:2:22;5548:29:32;;-1:-1:-1;;5587:31:32;;;;;5583:172;;;-1:-1:-1;;;5583:172:32;;;762:9:31;5676:31:32;;;;;5672:83;;;-1:-1:-1;762:9:31;5443:316:32;;;:::o;1185:1162::-;1331:66;1321:77;;1314:85;-1:-1:-1;;;;;1453:77:32;;1446:85;1443:1;1439:93;1422:111;-1:-1:-1;;;;;;;;;;;;;1587:77:32;;1580:85;1577:1;1573:93;1556:111;1731:66;1721:77;;1714:85;1711:1;1707:93;1690:111;1865:66;1855:77;;1848:85;1845:1;1841:93;1824:111;1999:66;1989:77;;1982:85;1979:1;1975:93;1958:111;2133:66;2123:77;;2116:85;2113:1;2109:93;2092:111;2267:66;2257:77;;;;2250:85;2247:1;2243:93;2226:111;;1290:1053::o;4119:193:27:-;4229:7;4251:56;4281:4;4287:2;4291:9;4302:4;4251:29;:56::i;3922:193::-;4032:7;4054:56;4084:2;4088:4;4094:9;4105:4;4054:29;:56::i;920:231::-;1059:19;1093:53;1105:5;1112:9;1123:5;1130:9;1141:4;1093:11;:53::i;4514:194::-;4624:7;4646:57;4676:4;4682:2;4686:9;4697:5;4646:29;:57::i;4316:194::-;4426:7;4448:57;4478:2;4482:4;4488:9;4499:5;4448:29;:57::i;1701:235::-;1842:19;1876:55;1888:5;1895:9;1906:6;1914:9;1925:5;1876:11;:55::i;887:586:33:-;1027:19;-1:-1:-1;;;;;1075:23:33;;;1054:44;;;1112:23;;;;1104:32;;;;;;-1:-1:-1;1207:42:33;;;-1:-1:-1;;;;;;;1207:42:33;1270:7;:198;;-1:-1:-1;;;;;1398:70:33;;;;:57;;1414:10;;1426:16;;1398:57;;:15;:57::i;:::-;:70;;;;;;1270:198;;;1286:103;1309:67;1335:10;1347:16;-1:-1:-1;;;;;1309:67:33;;:25;:67::i;:::-;-1:-1:-1;;;;;1286:103:33;;:22;:103::i;:::-;1256:212;887:586;-1:-1:-1;;;;;;;887:586:33:o;1923:404::-;2063:19;-1:-1:-1;;;;;2098:24:33;;;;;;;;2090:33;;;;;;2150:23;;;-1:-1:-1;;;;;2129:44:33;2193:7;:129;;2269:53;2285:10;-1:-1:-1;;;;;2269:53:33;;-1:-1:-1;;;2269:15:33;:53::i;:::-;2193:129;;;2203:63;2229:10;-1:-1:-1;;;;;2203:63:33;;-1:-1:-1;;;2203:25:33;:63::i;:::-;2179:143;1923:404;-1:-1:-1;;;;;;1923:404:33:o;1940:1978:27:-;2090:19;-1:-1:-1;;;;;2125:9:27;;2117:18;;;;;;-1:-1:-1;;;;;2149:13:27;;2141:22;;;;;;2187:9;2174:22;;:9;:22;;;2170:1744;;;2239:11;2235:29;;-1:-1:-1;2259:5:27;2252:12;;2235:29;-1:-1:-1;2299:42:27;;;-1:-1:-1;;;;;;;2299:42:27;2350:830;;;;-1:-1:-1;;;;;2404:44:27;;2415:14;;;;;;;2404:35;;;;;:44;2400:276;;;2484:26;;;2526:31;;;2522:116;;2574:63;2600:16;-1:-1:-1;;;;;2574:63:27;;2625:11;2574:25;:63::i;:::-;2559:79;;;;;;;2522:116;2400:276;;2701:80;2724:16;2742:38;2773:6;-1:-1:-1;;;;;2743:24:27;;2724:16;2743:24;;;;;;;2742:30;:38::i;:::-;2701:22;:80::i;:::-;2686:96;;;;;;2350:830;-1:-1:-1;;;;;2840:44:27;;2851:14;;;;;;;2840:35;;;;;:44;2832:53;;;;;;2986:7;2967:16;:26;2959:35;;;;;;3081:90;:78;3107:16;-1:-1:-1;;;;;3081:78:27;;3132:26;;;3081:25;:78::i;:::-;:88;:90::i;2170:1744::-;3354:9;3350:558;;;3392:184;:159;-1:-1:-1;;;;;3424:27:27;;;:126;;3501:49;3517:6;-1:-1:-1;;;;;;;;3501:49:27;;:15;:49::i;:::-;3424:126;;;-1:-1:-1;;;;;3454:44:27;;129:2:22;3455:30:27;;;3454:44;;;;;;3424:126;-1:-1:-1;;;;;3392:14:27;;;:31;:159::i;:184::-;3375:201;;;;3350:558;3601:16;-1:-1:-1;;;;;3620:27:27;;;:177;;3738:59;3764:6;-1:-1:-1;;;;;;;;3738:59:27;;:25;:59::i;:::-;3620:177;;;3660:65;129:2:22;3683:30:27;;;-1:-1:-1;;;;;3660:65:27;;:22;:65::i;:::-;3601:196;-1:-1:-1;;;;;;3816:16:27;;;-1:-1:-1;3808:25:27;;;;;;-1:-1:-1;;;;;3858:16:27;;;;-1:-1:-1;3843:32:27;;5064:149:24;5175:9;;;5189;;5186:16;;5171:32;;5158:51::o;482:105:28:-;580:1;-1:-1:-1;;;;;560:21:28;;;;552:30;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;
Swarm Source
none://164736f6c6343000706000a
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ZKEVM | 100.00% | $1 | 7,074.4136 | $7,074.61 |
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.