Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- AlgebraPoolDeployer
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 0
- EVM Version
- default
- Verified at
- 2024-03-11T10:34:46.948069Z
contracts/AlgebraPoolDeployer.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './interfaces/IAlgebraPoolDeployer.sol';
import './AlgebraPool.sol';
/// @title Algebra pool deployer
/// @notice Is used by AlgebraFactory to deploy pools
/// @dev Version: Algebra V1.9-directional-fee
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))}());
}
}
contracts/interfaces/IAlgebraPool.sol
// 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
}
contracts/AlgebraPool.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './interfaces/IAlgebraPool.sol';
import './interfaces/IDataStorageOperator.sol';
import './interfaces/IAlgebraVirtualPool.sol';
import './base/PoolState.sol';
import './base/PoolImmutables.sol';
import './libraries/TokenDeltaMath.sol';
import './libraries/PriceMovementMath.sol';
import './libraries/TickManager.sol';
import './libraries/TickTable.sol';
import './libraries/LowGasSafeMath.sol';
import './libraries/SafeCast.sol';
import './libraries/FullMath.sol';
import './libraries/Constants.sol';
import './libraries/TransferHelper.sol';
import './libraries/TickMath.sol';
import './libraries/LiquidityMath.sol';
import './interfaces/IAlgebraPoolDeployer.sol';
import './interfaces/IAlgebraFactory.sol';
import './interfaces/IERC20Minimal.sol';
import './interfaces/callback/IAlgebraMintCallback.sol';
import './interfaces/callback/IAlgebraSwapCallback.sol';
import './interfaces/callback/IAlgebraFlashCallback.sol';
/// @title Algebra concentrated liquidity pool
/// @notice This contract is responsible for liquidity positions, swaps and flashloans
/// @dev Version: Algebra V1.9-directional-fee
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.feeZto = Constants.BASE_FEE;
globalState.feeOtz = Constants.BASE_FEE;
tickSpacing = 60;
}
function balanceToken0() private view returns (uint256) {
return IERC20Minimal(token0).balanceOf(address(this));
}
function balanceToken1() private view returns (uint256) {
return IERC20Minimal(token1).balanceOf(address(this));
}
/// @inheritdoc IAlgebraPoolState
function timepoints(uint256 index)
external
view
override
returns (
bool initialized,
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint88 volatilityCumulative,
int24 averageTick,
uint144 volumePerLiquidityCumulative
)
{
return IDataStorageOperator(dataStorageOperator).timepoints(index);
}
struct Cumulatives {
int56 tickCumulative;
uint160 outerSecondPerLiquidity;
uint32 outerSecondsSpent;
}
/// @inheritdoc IAlgebraPoolDerivedState
function getInnerCumulatives(int24 bottomTick, int24 topTick)
external
view
override
onlyValidTicks(bottomTick, topTick)
returns (
int56 innerTickCumulative,
uint160 innerSecondsSpentPerLiquidity,
uint32 innerSecondsSpent
)
{
Cumulatives memory lower;
{
TickManager.Tick storage _lower = ticks[bottomTick];
(lower.tickCumulative, lower.outerSecondPerLiquidity, lower.outerSecondsSpent) = (
_lower.outerTickCumulative,
_lower.outerSecondsPerLiquidity,
_lower.outerSecondsSpent
);
require(_lower.initialized);
}
Cumulatives memory upper;
{
TickManager.Tick storage _upper = ticks[topTick];
(upper.tickCumulative, upper.outerSecondPerLiquidity, upper.outerSecondsSpent) = (
_upper.outerTickCumulative,
_upper.outerSecondsPerLiquidity,
_upper.outerSecondsSpent
);
require(_upper.initialized);
}
(int24 currentTick, uint16 currentTimepointIndex) = (globalState.tick, globalState.timepointIndex);
if (currentTick < bottomTick) {
return (
lower.tickCumulative - upper.tickCumulative,
lower.outerSecondPerLiquidity - upper.outerSecondPerLiquidity,
lower.outerSecondsSpent - upper.outerSecondsSpent
);
}
if (currentTick < topTick) {
uint32 globalTime = _blockTimestamp();
(int56 globalTickCumulative, uint160 globalSecondsPerLiquidityCumulative, , ) = _getSingleTimepoint(
globalTime,
0,
currentTick,
currentTimepointIndex,
liquidity
);
return (
globalTickCumulative - lower.tickCumulative - upper.tickCumulative,
globalSecondsPerLiquidityCumulative - lower.outerSecondPerLiquidity - upper.outerSecondPerLiquidity,
globalTime - lower.outerSecondsSpent - upper.outerSecondsSpent
);
}
return (
upper.tickCumulative - lower.tickCumulative,
upper.outerSecondPerLiquidity - lower.outerSecondPerLiquidity,
upper.outerSecondsSpent - lower.outerSecondsSpent
);
}
/// @inheritdoc IAlgebraPoolDerivedState
function getTimepoints(uint32[] calldata secondsAgos)
external
view
override
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
)
{
return
IDataStorageOperator(dataStorageOperator).getTimepoints(
_blockTimestamp(),
secondsAgos,
globalState.tick,
globalState.timepointIndex,
liquidity
);
}
/// @inheritdoc IAlgebraPoolActions
function initialize(uint160 initialPrice) external override {
require(globalState.price == 0, 'AI');
// getTickAtSqrtRatio checks validity of initialPrice inside
int24 tick = TickMath.getTickAtSqrtRatio(initialPrice);
uint8 defaultCommunityFee = IAlgebraFactory(factory).defaultCommunityFee();
uint32 timestamp = _blockTimestamp();
IDataStorageOperator(dataStorageOperator).initialize(timestamp, tick);
globalState.price = initialPrice;
globalState.communityFeeToken0 = defaultCommunityFee;
globalState.communityFeeToken1 = defaultCommunityFee;
globalState.unlocked = true;
globalState.tick = tick;
emit Initialize(initialPrice, tick);
}
/**
* @notice Increases amounts of tokens owed to owner of the position
* @param _position The position object to operate with
* @param liquidityDelta The amount on which to increase\decrease the liquidity
* @param innerFeeGrowth0Token Total fee token0 fee growth per 1/liquidity between position's lower and upper ticks
* @param innerFeeGrowth1Token Total fee token1 fee growth per 1/liquidity between position's lower and upper ticks
*/
function _recalculatePosition(
Position storage _position,
int128 liquidityDelta,
uint256 innerFeeGrowth0Token,
uint256 innerFeeGrowth1Token
) internal {
(uint128 currentLiquidity, uint32 lastLiquidityAddTimestamp) = (_position.liquidity, _position.lastLiquidityAddTimestamp);
if (liquidityDelta == 0) {
require(currentLiquidity > 0, 'NP'); // Do not recalculate the empty ranges
} else {
if (liquidityDelta < 0) {
uint32 _liquidityCooldown = liquidityCooldown;
if (_liquidityCooldown > 0) {
require((_blockTimestamp() - lastLiquidityAddTimestamp) >= _liquidityCooldown);
}
}
// change position liquidity
uint128 liquidityNext = LiquidityMath.addDelta(currentLiquidity, liquidityDelta);
(_position.liquidity, _position.lastLiquidityAddTimestamp) = (
liquidityNext,
liquidityNext > 0 ? (liquidityDelta > 0 ? _blockTimestamp() : lastLiquidityAddTimestamp) : 0
);
}
// update the position
uint256 _innerFeeGrowth0Token = _position.innerFeeGrowth0Token;
uint256 _innerFeeGrowth1Token = _position.innerFeeGrowth1Token;
uint128 fees0;
if (innerFeeGrowth0Token != _innerFeeGrowth0Token) {
_position.innerFeeGrowth0Token = innerFeeGrowth0Token;
fees0 = uint128(FullMath.mulDiv(innerFeeGrowth0Token - _innerFeeGrowth0Token, currentLiquidity, Constants.Q128));
}
uint128 fees1;
if (innerFeeGrowth1Token != _innerFeeGrowth1Token) {
_position.innerFeeGrowth1Token = innerFeeGrowth1Token;
fees1 = uint128(FullMath.mulDiv(innerFeeGrowth1Token - _innerFeeGrowth1Token, currentLiquidity, Constants.Q128));
}
// To avoid overflow owner has to collect fee before it
if (fees0 | fees1 != 0) {
_position.fees0 += fees0;
_position.fees1 += fees1;
}
}
struct UpdatePositionCache {
uint160 price; // The square root of the current price in Q64.96 format
int24 tick; // The current tick
uint16 timepointIndex; // The index of the last written timepoint
}
/**
* @dev Updates position's ticks and its fees
* @return position The Position object to operate with
* @return amount0 The amount of token0 the caller needs to send, negative if the pool needs to send it
* @return amount1 The amount of token1 the caller needs to send, negative if the pool needs to send it
*/
function _updatePositionTicksAndFees(
address owner,
int24 bottomTick,
int24 topTick,
int128 liquidityDelta
)
private
returns (
Position storage position,
int256 amount0,
int256 amount1
)
{
UpdatePositionCache memory cache = UpdatePositionCache(globalState.price, globalState.tick, globalState.timepointIndex);
position = getOrCreatePosition(owner, bottomTick, topTick);
(uint256 _totalFeeGrowth0Token, uint256 _totalFeeGrowth1Token) = (totalFeeGrowth0Token, totalFeeGrowth1Token);
bool toggledBottom;
bool toggledTop;
if (liquidityDelta != 0) {
uint32 time = _blockTimestamp();
(int56 tickCumulative, uint160 secondsPerLiquidityCumulative, , ) = _getSingleTimepoint(time, 0, cache.tick, cache.timepointIndex, liquidity);
if (
ticks.update(
bottomTick,
cache.tick,
liquidityDelta,
_totalFeeGrowth0Token,
_totalFeeGrowth1Token,
secondsPerLiquidityCumulative,
tickCumulative,
time,
false // isTopTick
)
) {
toggledBottom = true;
tickTable.toggleTick(bottomTick);
}
if (
ticks.update(
topTick,
cache.tick,
liquidityDelta,
_totalFeeGrowth0Token,
_totalFeeGrowth1Token,
secondsPerLiquidityCumulative,
tickCumulative,
time,
true // isTopTick
)
) {
toggledTop = true;
tickTable.toggleTick(topTick);
}
}
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getInnerFeeGrowth(
bottomTick,
topTick,
cache.tick,
_totalFeeGrowth0Token,
_totalFeeGrowth1Token
);
_recalculatePosition(position, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
if (liquidityDelta != 0) {
// if liquidityDelta is negative and the tick was toggled, it means that it should not be initialized anymore, so we delete it
if (liquidityDelta < 0) {
if (toggledBottom) delete ticks[bottomTick];
if (toggledTop) delete ticks[topTick];
}
int128 globalLiquidityDelta;
(amount0, amount1, globalLiquidityDelta) = _getAmountsForLiquidity(bottomTick, topTick, liquidityDelta, cache.tick, cache.price);
if (globalLiquidityDelta != 0) {
uint128 liquidityBefore = liquidity;
uint16 newTimepointIndex = _writeTimepoint(cache.timepointIndex, _blockTimestamp(), cache.tick, liquidityBefore, volumePerLiquidityInBlock);
if (cache.timepointIndex != newTimepointIndex) {
_updateFee(_blockTimestamp(), cache.tick, newTimepointIndex, liquidityBefore);
globalState.timepointIndex = newTimepointIndex;
volumePerLiquidityInBlock = 0;
}
liquidity = LiquidityMath.addDelta(liquidityBefore, liquidityDelta);
}
}
}
function _getAmountsForLiquidity(
int24 bottomTick,
int24 topTick,
int128 liquidityDelta,
int24 currentTick,
uint160 currentPrice
)
private
pure
returns (
int256 amount0,
int256 amount1,
int128 globalLiquidityDelta
)
{
// If current tick is less than the provided bottom one then only the token0 has to be provided
if (currentTick < bottomTick) {
amount0 = TokenDeltaMath.getToken0Delta(TickMath.getSqrtRatioAtTick(bottomTick), TickMath.getSqrtRatioAtTick(topTick), liquidityDelta);
} else if (currentTick < topTick) {
amount0 = TokenDeltaMath.getToken0Delta(currentPrice, TickMath.getSqrtRatioAtTick(topTick), liquidityDelta);
amount1 = TokenDeltaMath.getToken1Delta(TickMath.getSqrtRatioAtTick(bottomTick), currentPrice, liquidityDelta);
globalLiquidityDelta = liquidityDelta;
}
// If current tick is greater than the provided top one then only the token1 has to be provided
else {
amount1 = TokenDeltaMath.getToken1Delta(TickMath.getSqrtRatioAtTick(bottomTick), TickMath.getSqrtRatioAtTick(topTick), liquidityDelta);
}
}
/**
* @notice This function fetches certain position object
* @param owner The address owing the position
* @param bottomTick The position's bottom tick
* @param topTick The position's top tick
* @return position The Position object
*/
function getOrCreatePosition(
address owner,
int24 bottomTick,
int24 topTick
) private view returns (Position storage) {
bytes32 key;
assembly {
key := or(shl(24, or(shl(24, owner), and(bottomTick, 0xFFFFFF))), and(topTick, 0xFFFFFF))
}
return positions[key];
}
/// @inheritdoc IAlgebraPoolActions
function mint(
address sender,
address recipient,
int24 bottomTick,
int24 topTick,
uint128 liquidityDesired,
bytes calldata data
)
external
override
lock
onlyValidTicks(bottomTick, topTick)
returns (
uint256 amount0,
uint256 amount1,
uint128 liquidityActual
)
{
require(liquidityDesired > 0, 'IL');
{
int24 _tickSpacing = tickSpacing;
require(bottomTick % _tickSpacing | topTick % _tickSpacing == 0, 'tick is not spaced'); // ensure that the tick is spaced
}
{
(int256 amount0Int, int256 amount1Int, ) = _getAmountsForLiquidity(
bottomTick,
topTick,
int256(liquidityDesired).toInt128(),
globalState.tick,
globalState.price
);
amount0 = uint256(amount0Int);
amount1 = uint256(amount1Int);
}
uint256 receivedAmount0;
uint256 receivedAmount1;
{
if (amount0 > 0) receivedAmount0 = balanceToken0();
if (amount1 > 0) receivedAmount1 = balanceToken1();
IAlgebraMintCallback(msg.sender).algebraMintCallback(amount0, amount1, data);
if (amount0 > 0) require((receivedAmount0 = balanceToken0() - receivedAmount0) > 0, 'IIAM');
if (amount1 > 0) require((receivedAmount1 = balanceToken1() - receivedAmount1) > 0, 'IIAM');
}
liquidityActual = liquidityDesired;
if (receivedAmount0 < amount0) {
liquidityActual = uint128(FullMath.mulDiv(uint256(liquidityDesired), receivedAmount0, amount0));
}
if (receivedAmount1 < amount1) {
uint128 liquidityForRA1 = uint128(FullMath.mulDiv(uint256(liquidityDesired), receivedAmount1, amount1));
if (liquidityForRA1 < liquidityActual) {
liquidityActual = liquidityForRA1;
}
}
require(liquidityActual > 0, 'IIL2');
{
(, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees(recipient, bottomTick, topTick, int256(liquidityActual).toInt128());
require((amount0 = uint256(amount0Int)) <= receivedAmount0, 'IIAM2');
require((amount1 = uint256(amount1Int)) <= receivedAmount1, 'IIAM2');
}
if (receivedAmount0 > amount0) {
TransferHelper.safeTransfer(token0, sender, receivedAmount0 - amount0);
}
if (receivedAmount1 > amount1) {
TransferHelper.safeTransfer(token1, sender, receivedAmount1 - amount1);
}
emit Mint(msg.sender, recipient, bottomTick, topTick, liquidityActual, amount0, amount1);
}
/// @inheritdoc IAlgebraPoolActions
function collect(
address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount0Requested,
uint128 amount1Requested
) external override lock returns (uint128 amount0, uint128 amount1) {
Position storage position = getOrCreatePosition(msg.sender, bottomTick, topTick);
(uint128 positionFees0, uint128 positionFees1) = (position.fees0, position.fees1);
amount0 = amount0Requested > positionFees0 ? positionFees0 : amount0Requested;
amount1 = amount1Requested > positionFees1 ? positionFees1 : amount1Requested;
if (amount0 | amount1 != 0) {
position.fees0 = positionFees0 - amount0;
position.fees1 = positionFees1 - amount1;
if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0);
if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1);
}
emit Collect(msg.sender, recipient, bottomTick, topTick, amount0, amount1);
}
/// @inheritdoc IAlgebraPoolActions
function burn(
int24 bottomTick,
int24 topTick,
uint128 amount
) external override lock onlyValidTicks(bottomTick, topTick) returns (uint256 amount0, uint256 amount1) {
(Position storage position, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees(
msg.sender,
bottomTick,
topTick,
-int256(amount).toInt128()
);
amount0 = uint256(-amount0Int);
amount1 = uint256(-amount1Int);
if (amount0 | amount1 != 0) {
(position.fees0, position.fees1) = (position.fees0.add128(uint128(amount0)), position.fees1.add128(uint128(amount1)));
}
emit Burn(msg.sender, bottomTick, topTick, amount, amount0, amount1);
}
/// @dev Updates fees according combinations of sigmoids
function _updateFee(
uint32 _time,
int24 _tick,
uint16 _index,
uint128 _liquidity
) private returns (uint16 newFeeZto, uint16 newFeeOtz) {
(newFeeZto, newFeeOtz) = IDataStorageOperator(dataStorageOperator).getFees(_time, _tick, _index, _liquidity);
(globalState.feeZto, globalState.feeOtz) = (newFeeZto, newFeeOtz);
emit Fee(newFeeZto, newFeeOtz);
}
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 = zeroToOne ? globalState.feeZto : globalState.feeOtz;
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;
if (zeroToOne) {
(cache.fee, ) = _updateFee(blockTimestamp, currentTick, newTimepointIndex, currentLiquidity);
} else {
(, cache.fee) = _updateFee(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.timepointIndex) = (currentPrice, currentTick, 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 = Constants.BASE_FEE;
uint256 fee0;
uint256 balance0Before = balanceToken0();
if (amount0 > 0) {
fee0 = FullMath.mulDivRoundingUp(amount0, _fee, 1e6);
TransferHelper.safeTransfer(token0, recipient, amount0);
}
uint256 fee1;
uint256 balance1Before = balanceToken1();
if (amount1 > 0) {
fee1 = FullMath.mulDivRoundingUp(amount1, _fee, 1e6);
TransferHelper.safeTransfer(token1, recipient, amount1);
}
IAlgebraFlashCallback(msg.sender).algebraFlashCallback(fee0, fee1, data);
address vault = IAlgebraFactory(factory).vaultAddress();
uint256 paid0 = balanceToken0();
require(balance0Before.add(fee0) <= paid0, 'F0');
paid0 -= balance0Before;
if (paid0 > 0) {
uint8 _communityFeeToken0 = globalState.communityFeeToken0;
uint256 fees0;
if (_communityFeeToken0 > 0) {
fees0 = (paid0 * _communityFeeToken0) / Constants.COMMUNITY_FEE_DENOMINATOR;
TransferHelper.safeTransfer(token0, vault, fees0);
}
totalFeeGrowth0Token += FullMath.mulDiv(paid0 - fees0, Constants.Q128, _liquidity);
}
uint256 paid1 = balanceToken1();
require(balance1Before.add(fee1) <= paid1, 'F1');
paid1 -= balance1Before;
if (paid1 > 0) {
uint8 _communityFeeToken1 = globalState.communityFeeToken1;
uint256 fees1;
if (_communityFeeToken1 > 0) {
fees1 = (paid1 * _communityFeeToken1) / Constants.COMMUNITY_FEE_DENOMINATOR;
TransferHelper.safeTransfer(token1, vault, fees1);
}
totalFeeGrowth1Token += FullMath.mulDiv(paid1 - fees1, Constants.Q128, _liquidity);
}
emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1);
}
/// @inheritdoc IAlgebraPoolPermissionedActions
function setCommunityFee(uint8 communityFee0, uint8 communityFee1) external override lock onlyFactoryOwner {
require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE));
(globalState.communityFeeToken0, globalState.communityFeeToken1) = (communityFee0, communityFee1);
emit CommunityFee(communityFee0, communityFee1);
}
/// @inheritdoc IAlgebraPoolPermissionedActions
function setTickSpacing(int24 newTickSpacing) external override lock onlyFactoryOwner {
require(newTickSpacing > 0 && newTickSpacing <= Constants.MAX_TICK_SPACING && tickSpacing != newTickSpacing, 'Invalid newTickSpacing');
tickSpacing = newTickSpacing;
emit TickSpacing(newTickSpacing);
}
/// @inheritdoc IAlgebraPoolPermissionedActions
function setIncentive(address virtualPoolAddress) external override {
require(msg.sender == IAlgebraFactory(factory).farmingAddress());
activeIncentive = virtualPoolAddress;
emit Incentive(virtualPoolAddress);
}
/// @inheritdoc IAlgebraPoolPermissionedActions
function setLiquidityCooldown(uint32 newLiquidityCooldown) external override onlyFactoryOwner {
require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown);
liquidityCooldown = newLiquidityCooldown;
emit LiquidityCooldown(newLiquidityCooldown);
}
}
contracts/base/PoolImmutables.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import '../interfaces/pool/IAlgebraPoolImmutables.sol';
import '../interfaces/IAlgebraPoolDeployer.sol';
import '../libraries/Constants.sol';
abstract contract PoolImmutables is IAlgebraPoolImmutables {
/// @inheritdoc IAlgebraPoolImmutables
address public immutable override dataStorageOperator;
/// @inheritdoc IAlgebraPoolImmutables
address public immutable override factory;
/// @inheritdoc IAlgebraPoolImmutables
address public immutable override token0;
/// @inheritdoc IAlgebraPoolImmutables
address public immutable override token1;
/// @inheritdoc IAlgebraPoolImmutables
function maxLiquidityPerTick() external pure override returns (uint128) {
return Constants.MAX_LIQUIDITY_PER_TICK;
}
constructor(address deployer) {
(dataStorageOperator, factory, token0, token1) = IAlgebraPoolDeployer(deployer).parameters();
}
}
contracts/base/PoolState.sol
// 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 feeZto; // The current fee for ZtO swap in hundredths of a bip, i.e. 1e-6
uint16 feeOtz; // The current fee for OtZ swap in hundredths of a bip, i.e. 1e-6
uint16 timepointIndex; // The index of the last written timepoint
uint8 communityFeeToken0; // The community fee represented as a percent of all collected fee in thousandths (1e-3)
uint8 communityFeeToken1;
bool unlocked; // True if the contract is unlocked, otherwise - false
}
/// @inheritdoc IAlgebraPoolState
uint256 public override totalFeeGrowth0Token;
/// @inheritdoc IAlgebraPoolState
uint256 public override totalFeeGrowth1Token;
/// @inheritdoc IAlgebraPoolState
GlobalState public override globalState;
/// @inheritdoc IAlgebraPoolState
uint128 public override liquidity;
uint128 internal volumePerLiquidityInBlock;
/// @inheritdoc IAlgebraPoolState
uint32 public override liquidityCooldown;
/// @inheritdoc IAlgebraPoolState
address public override activeIncentive;
/// @inheritdoc IAlgebraPoolState
int24 public override tickSpacing;
/// @inheritdoc IAlgebraPoolState
mapping(int24 => TickManager.Tick) public override ticks;
/// @inheritdoc IAlgebraPoolState
mapping(int16 => uint256) public override tickTable;
/// @dev Reentrancy protection. Implemented in every function of the contract since there are checks of balances.
modifier lock() {
require(globalState.unlocked, 'LOK');
globalState.unlocked = false;
_;
globalState.unlocked = true;
}
/// @dev This function is created for testing by overriding it.
/// @return A timestamp converted to uint32
function _blockTimestamp() internal view virtual returns (uint32) {
return uint32(block.timestamp); // truncation is desired
}
}
contracts/interfaces/IAlgebraFactory.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title The interface for the Algebra Factory
* @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraFactory {
/**
* @notice Emitted when the owner of the factory is changed
* @param newOwner The owner after the owner was changed
*/
event Owner(address indexed newOwner);
/**
* @notice Emitted when the vault address is changed
* @param newVaultAddress The vault address after the address was changed
*/
event VaultAddress(address indexed newVaultAddress);
/**
* @notice Emitted when a pool is created
* @param token0 The first token of the pool by address sort order
* @param token1 The second token of the pool by address sort order
* @param pool The address of the created pool
*/
event Pool(address indexed token0, address indexed token1, address pool);
/**
* @notice Emitted when the farming address is changed
* @param newFarmingAddress The farming address after the address was changed
*/
event FarmingAddress(address indexed newFarmingAddress);
/**
* @notice Emitted when the default community fee is changed
* @param newDefaultCommunityFee The new default community fee value
*/
event DefaultCommunityFee(uint8 newDefaultCommunityFee);
event FeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
);
/**
* @notice Returns the current owner of the factory
* @dev Can be changed by the current owner via setOwner
* @return The address of the factory owner
*/
function owner() external view returns (address);
/**
* @notice Returns the current poolDeployerAddress
* @return The address of the poolDeployer
*/
function poolDeployer() external view returns (address);
/**
* @dev Is retrieved from the pools to restrict calling
* certain functions not by a tokenomics contract
* @return The tokenomics contract address
*/
function farmingAddress() external view returns (address);
/**
* @notice Returns the default community fee
* @return Fee which will be set at the creation of the pool
*/
function defaultCommunityFee() external view returns (uint8);
function vaultAddress() external view returns (address);
/**
* @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
* @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
* @param tokenA The contract address of either token0 or token1
* @param tokenB The contract address of the other token
* @return pool The pool address
*/
function poolByPair(address tokenA, address tokenB) external view returns (address pool);
/**
* @notice Creates a pool for the given two tokens and fee
* @param tokenA One of the two tokens in the desired pool
* @param tokenB The other of the two tokens in the desired pool
* @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
* from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
* are invalid.
* @return pool The address of the newly created pool
*/
function createPool(address tokenA, address tokenB) external returns (address pool);
/**
* @notice Updates the owner of the factory
* @dev Must be called by the current owner
* @param _owner The new owner of the factory
*/
function setOwner(address _owner) external;
/**
* @dev updates tokenomics address on the factory
* @param _farmingAddress The new tokenomics contract address
*/
function setFarmingAddress(address _farmingAddress) external;
/**
* @dev updates default community fee for new pools
* @param newDefaultCommunityFee The new community fee, _must_ be <= MAX_COMMUNITY_FEE
*/
function setDefaultCommunityFee(uint8 newDefaultCommunityFee) external;
/**
* @dev updates vault address on the factory
* @param _vaultAddress The new vault contract address
*/
function setVaultAddress(address _vaultAddress) external;
/**
* @notice Changes initial fee configuration for new pools
* @dev changes coefficients for sigmoids: α / (1 + e^( (β-x) / γ))
* alpha1 + alpha2 + baseFee (max possible fee) must be <= type(uint16).max
* gammas must be > 0
* @param alpha1 max value of the first sigmoid
* @param alpha2 max value of the second sigmoid
* @param beta1 shift along the x-axis for the first sigmoid
* @param beta2 shift along the x-axis for the second sigmoid
* @param gamma1 horizontal stretch factor for the first sigmoid
* @param gamma2 horizontal stretch factor for the second sigmoid
* @param volumeBeta shift along the x-axis for the outer volume-sigmoid
* @param volumeGamma horizontal stretch factor the outer volume-sigmoid
* @param baseFee minimum possible fee
*/
function setBaseFeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
) external;
}
contracts/interfaces/IAlgebraPoolDeployer.sol
// 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;
}
contracts/interfaces/IAlgebraVirtualPool.sol
// 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);
}
contracts/interfaces/IDataStorageOperator.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;
import '../libraries/AdaptiveFee.sol';
interface IDataStorageOperator {
event FeeConfiguration(bool zto, AdaptiveFee.Configuration feeConfig);
/**
* @notice Returns data belonging to a certain timepoint
* @param index The index of timepoint in the array
* @dev There is more convenient function to fetch a timepoint: getTimepoints(). Which requires not an index but seconds
* @return initialized Whether the timepoint has been initialized and the values are safe to use,
* blockTimestamp The timestamp of the observation,
* tickCumulative The tick multiplied by seconds elapsed for the life of the pool as of the timepoint timestamp,
* secondsPerLiquidityCumulative The seconds per in range liquidity for the life of the pool as of the timepoint timestamp,
* volatilityCumulative Cumulative standard deviation for the life of the pool as of the timepoint timestamp,
* averageTick Time-weighted average tick,
* volumePerLiquidityCumulative Cumulative swap volume per liquidity for the life of the pool as of the timepoint timestamp
*/
function timepoints(uint256 index)
external
view
returns (
bool initialized,
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint88 volatilityCumulative,
int24 averageTick,
uint144 volumePerLiquidityCumulative
);
/// @notice Initialize the dataStorage array by writing the first slot. Called once for the lifecycle of the timepoints array
/// @param time The time of the dataStorage initialization, via block.timestamp truncated to uint32
/// @param tick Initial tick
function initialize(uint32 time, int24 tick) external;
/// @dev Reverts if an timepoint at or before the desired timepoint timestamp does not exist.
/// 0 may be passed as `secondsAgo' to return the current cumulative values.
/// If called with a timestamp falling between two timepoints, returns the counterfactual accumulator values
/// at exactly the timestamp between the two timepoints.
/// @param time The current block timestamp
/// @param secondsAgo The amount of time to look back, in seconds, at which point to return an timepoint
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return tickCumulative The cumulative tick since the pool was first initialized, as of `secondsAgo`
/// @return secondsPerLiquidityCumulative The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of `secondsAgo`
/// @return volatilityCumulative The cumulative volatility value since the pool was first initialized, as of `secondsAgo`
/// @return volumePerAvgLiquidity The cumulative volume per liquidity value since the pool was first initialized, as of `secondsAgo`
function getSingleTimepoint(
uint32 time,
uint32 secondsAgo,
int24 tick,
uint16 index,
uint128 liquidity
)
external
view
returns (
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint112 volatilityCumulative,
uint256 volumePerAvgLiquidity
);
/// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
/// @dev Reverts if `secondsAgos` > oldest timepoint
/// @param time The current block.timestamp
/// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an timepoint
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return tickCumulatives The cumulative tick since the pool was first initialized, as of each `secondsAgo`
/// @return secondsPerLiquidityCumulatives The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo`
/// @return volatilityCumulatives The cumulative volatility values since the pool was first initialized, as of each `secondsAgo`
/// @return volumePerAvgLiquiditys The cumulative volume per liquidity values since the pool was first initialized, as of each `secondsAgo`
function getTimepoints(
uint32 time,
uint32[] memory secondsAgos,
int24 tick,
uint16 index,
uint128 liquidity
)
external
view
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
);
/// @notice Returns average volatility in the range from time-WINDOW to time
/// @param time The current block.timestamp
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return TWVolatilityAverage The average volatility in the recent range
/// @return TWVolumePerLiqAverage The average volume per liquidity in the recent range
function getAverages(
uint32 time,
int24 tick,
uint16 index,
uint128 liquidity
) external view returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage);
/// @notice Writes an dataStorage timepoint to the array
/// @dev Writable at most once per block. Index represents the most recently written element. index must be tracked externally.
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param blockTimestamp The timestamp of the new timepoint
/// @param tick The active tick at the time of the new timepoint
/// @param liquidity The total in-range liquidity at the time of the new timepoint
/// @param volumePerLiquidity The gmean(volumes)/liquidity at the time of the new timepoint
/// @return indexUpdated The new index of the most recently written element in the dataStorage array
function write(
uint16 index,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint128 volumePerLiquidity
) external returns (uint16 indexUpdated);
/// @notice Changes fee configuration for the pool
function changeFeeConfiguration(bool zto, 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 feeZto The fee for ZtO swaps in hundredths of a bip, i.e. 1e-6
/// @return feeOtz The fee for OtZ swaps in hundredths of a bip, i.e. 1e-6
function getFees(
uint32 time,
int24 tick,
uint16 index,
uint128 liquidity
) external view returns (uint16 feeZto, uint16 feeOtz);
}
contracts/interfaces/IERC20Minimal.sol
// 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);
}
contracts/interfaces/callback/IAlgebraFlashCallback.sol
// 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;
}
contracts/interfaces/callback/IAlgebraMintCallback.sol
// 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;
}
contracts/interfaces/callback/IAlgebraSwapCallback.sol
// 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;
}
contracts/interfaces/pool/IAlgebraPoolActions.sol
// 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;
}
contracts/interfaces/pool/IAlgebraPoolDerivedState.sol
// 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
);
}
contracts/interfaces/pool/IAlgebraPoolEvents.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Events emitted by a pool
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolEvents {
/**
* @notice Emitted exactly once by a pool when #initialize is first called on the pool
* @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
* @param price The initial sqrt price of the pool, as a Q64.96
* @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
*/
event Initialize(uint160 price, int24 tick);
/**
* @notice Emitted when liquidity is minted for a given position
* @param sender The address that minted the liquidity
* @param owner The owner of the position and recipient of any minted liquidity
* @param bottomTick The lower tick of the position
* @param topTick The upper tick of the position
* @param liquidityAmount The amount of liquidity minted to the position range
* @param amount0 How much token0 was required for the minted liquidity
* @param amount1 How much token1 was required for the minted liquidity
*/
event Mint(
address sender,
address indexed owner,
int24 indexed bottomTick,
int24 indexed topTick,
uint128 liquidityAmount,
uint256 amount0,
uint256 amount1
);
/**
* @notice Emitted when fees are collected by the owner of a position
* @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
* @param owner The owner of the position for which fees are collected
* @param recipient The address that received fees
* @param bottomTick The lower tick of the position
* @param topTick The upper tick of the position
* @param amount0 The amount of token0 fees collected
* @param amount1 The amount of token1 fees collected
*/
event Collect(address indexed owner, address recipient, int24 indexed bottomTick, int24 indexed topTick, uint128 amount0, uint128 amount1);
/**
* @notice Emitted when a position's liquidity is removed
* @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
* @param owner The owner of the position for which liquidity is removed
* @param bottomTick The lower tick of the position
* @param topTick The upper tick of the position
* @param liquidityAmount The amount of liquidity to remove
* @param amount0 The amount of token0 withdrawn
* @param amount1 The amount of token1 withdrawn
*/
event Burn(address indexed owner, int24 indexed bottomTick, int24 indexed topTick, uint128 liquidityAmount, uint256 amount0, uint256 amount1);
/**
* @notice Emitted by the pool for any swaps between token0 and token1
* @param sender The address that initiated the swap call, and that received the callback
* @param recipient The address that received the output of the swap
* @param amount0 The delta of the token0 balance of the pool
* @param amount1 The delta of the token1 balance of the pool
* @param price The sqrt(price) of the pool after the swap, as a Q64.96
* @param liquidity The liquidity of the pool after the swap
* @param tick The log base 1.0001 of price of the pool after the swap
*/
event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 price, uint128 liquidity, int24 tick);
/**
* @notice Emitted by the pool for any flashes of token0/token1
* @param sender The address that initiated the swap call, and that received the callback
* @param recipient The address that received the tokens from flash
* @param amount0 The amount of token0 that was flashed
* @param amount1 The amount of token1 that was flashed
* @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
* @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
*/
event Flash(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1);
/**
* @notice Emitted when the community fee is changed by the pool
* @param communityFee0New The updated value of the token0 community fee percent
* @param communityFee1New The updated value of the token1 community fee percent
*/
event CommunityFee(uint8 communityFee0New, uint8 communityFee1New);
/**
* @notice Emitted when the tick spacing changes
* @param newTickSpacing The updated value of the new tick spacing
*/
event TickSpacing(int24 newTickSpacing);
/**
* @notice Emitted when new activeIncentive is set
* @param virtualPoolAddress The address of a virtual pool associated with the current active incentive
*/
event Incentive(address indexed virtualPoolAddress);
/**
* @notice Emitted when the fee changes
* @param feeZto The value of the token fee for zto swaps
* @param feeOtz The value of the token fee for otz swaps
*/
event Fee(uint16 feeZto, uint16 feeOtz);
/**
* @notice Emitted when the LiquidityCooldown changes
* @param liquidityCooldown The value of locktime for added liquidity
*/
event LiquidityCooldown(uint32 liquidityCooldown);
}
contracts/interfaces/pool/IAlgebraPoolImmutables.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '../IDataStorageOperator.sol';
/// @title Pool state that never changes
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolImmutables {
/**
* @notice The contract that stores all the timepoints and can perform actions with them
* @return The operator address
*/
function dataStorageOperator() external view returns (address);
/**
* @notice The contract that deployed the pool, which must adhere to the IAlgebraFactory interface
* @return The contract address
*/
function factory() external view returns (address);
/**
* @notice The first of the two tokens of the pool, sorted by address
* @return The token contract address
*/
function token0() external view returns (address);
/**
* @notice The second of the two tokens of the pool, sorted by address
* @return The token contract address
*/
function token1() external view returns (address);
/**
* @notice The maximum amount of position liquidity that can use any tick in the range
* @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
* also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
* @return The max amount of liquidity per tick
*/
function maxLiquidityPerTick() external view returns (uint128);
}
contracts/interfaces/pool/IAlgebraPoolPermissionedActions.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title Permissioned pool actions
* @notice Contains pool methods that may only be called by the factory owner or tokenomics
* @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraPoolPermissionedActions {
/**
* @notice Set the community's % share of the fees. Cannot exceed 25% (250)
* @param communityFee0 new community fee percent for token0 of the pool in thousandths (1e-3)
* @param communityFee1 new community fee percent for token1 of the pool in thousandths (1e-3)
*/
function setCommunityFee(uint8 communityFee0, uint8 communityFee1) external;
/// @notice Set the new tick spacing values. Only factory owner
/// @param newTickSpacing The new tick spacing value
function setTickSpacing(int24 newTickSpacing) external;
/**
* @notice Sets an active incentive
* @param virtualPoolAddress The address of a virtual pool associated with the incentive
*/
function setIncentive(address virtualPoolAddress) external;
/**
* @notice Sets new lock time for added liquidity
* @param newLiquidityCooldown The time in seconds
*/
function setLiquidityCooldown(uint32 newLiquidityCooldown) external;
}
contracts/interfaces/pool/IAlgebraPoolState.sol
// 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 feeZto The last pool fee value for ZtO swaps in hundredths of a bip, i.e. 1e-6;
* Returns feeOtz The last pool fee value for OtZ swaps 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 feeZto,
uint16 feeOtz,
uint16 timepointIndex,
uint8 communityFeeToken0,
uint8 communityFeeToken1,
bool unlocked
);
/**
* @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
* @dev This value can overflow the uint256
*/
function totalFeeGrowth0Token() external view returns (uint256);
/**
* @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
* @dev This value can overflow the uint256
*/
function totalFeeGrowth1Token() external view returns (uint256);
/**
* @notice The currently in range liquidity available to the pool
* @dev This value has no relationship to the total liquidity across all ticks.
* Returned value cannot exceed type(uint128).max
*/
function liquidity() external view returns (uint128);
/**
* @notice Look up information about a specific tick in the pool
* @dev This is a public structure, so the `return` natspec tags are omitted.
* @param tick The tick to look up
* @return liquidityTotal the total amount of position liquidity that uses the pool either as tick lower or
* tick upper;
* Returns liquidityDelta how much liquidity changes when the pool price crosses the tick;
* Returns outerFeeGrowth0Token the fee growth on the other side of the tick from the current tick in token0;
* Returns outerFeeGrowth1Token the fee growth on the other side of the tick from the current tick in token1;
* Returns outerTickCumulative the cumulative tick value on the other side of the tick from the current tick;
* Returns outerSecondsPerLiquidity the seconds spent per liquidity on the other side of the tick from the current tick;
* Returns outerSecondsSpent the seconds spent on the other side of the tick from the current tick;
* Returns initialized Set to true if the tick is initialized, i.e. liquidityTotal is greater than 0
* otherwise equal to false. Outside values can only be used if the tick is initialized.
* In addition, these values are only relative and must be used only in comparison to previous snapshots for
* a specific position.
*/
function ticks(int24 tick)
external
view
returns (
uint128 liquidityTotal,
int128 liquidityDelta,
uint256 outerFeeGrowth0Token,
uint256 outerFeeGrowth1Token,
int56 outerTickCumulative,
uint160 outerSecondsPerLiquidity,
uint32 outerSecondsSpent,
bool initialized
);
/** @notice Returns 256 packed tick initialized boolean values. See TickTable for more information */
function tickTable(int16 wordPosition) external view returns (uint256);
/**
* @notice Returns the information about a position by the position's key
* @dev This is a public mapping of structures, so the `return` natspec tags are omitted.
* @param key The position's key is a hash of a preimage composed by the owner, bottomTick and topTick
* @return liquidityAmount The amount of liquidity in the position;
* Returns lastLiquidityAddTimestamp Timestamp of last adding of liquidity;
* Returns innerFeeGrowth0Token Fee growth of token0 inside the tick range as of the last mint/burn/poke;
* Returns innerFeeGrowth1Token Fee growth of token1 inside the tick range as of the last mint/burn/poke;
* Returns fees0 The computed amount of token0 owed to the position as of the last mint/burn/poke;
* Returns fees1 The computed amount of token1 owed to the position as of the last mint/burn/poke
*/
function positions(bytes32 key)
external
view
returns (
uint128 liquidityAmount,
uint32 lastLiquidityAddTimestamp,
uint256 innerFeeGrowth0Token,
uint256 innerFeeGrowth1Token,
uint128 fees0,
uint128 fees1
);
/**
* @notice Returns data about a specific timepoint index
* @param index The element of the timepoints array to fetch
* @dev You most likely want to use #getTimepoints() instead of this method to get an timepoint as of some amount of time
* ago, rather than at a specific index in the array.
* This is a public mapping of structures, so the `return` natspec tags are omitted.
* @return initialized whether the timepoint has been initialized and the values are safe to use;
* Returns blockTimestamp The timestamp of the timepoint;
* Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the timepoint timestamp;
* Returns secondsPerLiquidityCumulative the seconds per in range liquidity for the life of the pool as of the timepoint timestamp;
* Returns volatilityCumulative Cumulative standard deviation for the life of the pool as of the timepoint timestamp;
* Returns averageTick Time-weighted average tick;
* Returns volumePerLiquidityCumulative Cumulative swap volume per liquidity for the life of the pool as of the timepoint timestamp;
*/
function timepoints(uint256 index)
external
view
returns (
bool initialized,
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint88 volatilityCumulative,
int24 averageTick,
uint144 volumePerLiquidityCumulative
);
/**
* @notice Returns the information about active incentive
* @dev if there is no active incentive at the moment, virtualPool,endTimestamp,startTimestamp would be equal to 0
* @return virtualPool The address of a virtual pool associated with the current active incentive
*/
function activeIncentive() external view returns (address virtualPool);
/**
* @notice Returns the lock time for added liquidity
*/
function liquidityCooldown() external view returns (uint32 cooldownInSeconds);
/**
* @notice The pool tick spacing
* @dev Ticks can only be used at multiples of this value
* e.g.: a tickSpacing of 60 means ticks can be initialized every 60th tick, i.e., ..., -120, -60, 0, 60, 120, ...
* This value is an int24 to avoid casting even though it is always positive.
* @return The tick spacing
*/
function tickSpacing() external view returns (int24);
}
contracts/libraries/AdaptiveFee.sol
// 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);
}
}
contracts/libraries/Constants.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
library Constants {
uint8 internal constant RESOLUTION = 96;
uint256 internal constant Q96 = 0x1000000000000000000000000;
uint256 internal constant Q128 = 0x100000000000000000000000000000000;
// fee value in hundredths of a bip, i.e. 1e-6
uint16 internal constant BASE_FEE = 100;
int24 internal constant MAX_TICK_SPACING = 500;
// max(uint128) / (MAX_TICK - MIN_TICK)
uint128 internal constant MAX_LIQUIDITY_PER_TICK = 191757638537527648490752896198553;
uint32 internal constant MAX_LIQUIDITY_COOLDOWN = 1 days;
uint8 internal constant MAX_COMMUNITY_FEE = 250;
uint256 internal constant COMMUNITY_FEE_DENOMINATOR = 1000;
}
contracts/libraries/FullMath.sol
// 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))
}
}
}
contracts/libraries/LiquidityMath.sol
// 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');
}
}
}
contracts/libraries/LowGasSafeMath.sol
// 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);
}
}
contracts/libraries/PriceMovementMath.sol
// 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);
}
}
}
contracts/libraries/SafeCast.sol
// 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);
}
}
contracts/libraries/TickManager.sol
// 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;
}
}
contracts/libraries/TickMath.sol
// 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;
}
}
contracts/libraries/TickTable.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './TickMath.sol';
/// @title Packed tick initialized state library
/// @notice Stores a packed mapping of tick index to its initialized state
/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.
library TickTable {
/// @notice Toggles the initialized state for a given tick from false to true, or vice versa
/// @param self The mapping in which to toggle the tick
/// @param tick The tick to toggle
function toggleTick(mapping(int16 => uint256) storage self, int24 tick) internal {
int16 rowNumber;
uint8 bitNumber;
assembly {
bitNumber := and(tick, 0xFF)
rowNumber := shr(8, tick)
}
self[rowNumber] ^= 1 << bitNumber;
}
/// @notice get position of single 1-bit
/// @dev it is assumed that word contains exactly one 1-bit, otherwise the result will be incorrect
/// @param word The word containing only one 1-bit
function getSingleSignificantBit(uint256 word) internal pure returns (uint8 singleBitPos) {
assembly {
singleBitPos := iszero(and(word, 0x5555555555555555555555555555555555555555555555555555555555555555))
singleBitPos := or(singleBitPos, shl(7, iszero(and(word, 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))))
singleBitPos := or(singleBitPos, shl(6, iszero(and(word, 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF))))
singleBitPos := or(singleBitPos, shl(5, iszero(and(word, 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF))))
singleBitPos := or(singleBitPos, shl(4, iszero(and(word, 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF))))
singleBitPos := or(singleBitPos, shl(3, iszero(and(word, 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF))))
singleBitPos := or(singleBitPos, shl(2, iszero(and(word, 0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F))))
singleBitPos := or(singleBitPos, shl(1, iszero(and(word, 0x3333333333333333333333333333333333333333333333333333333333333333))))
}
}
/// @notice get position of most significant 1-bit (leftmost)
/// @dev it is assumed that before the call, a check will be made that the argument (word) is not equal to zero
/// @param word The word containing at least one 1-bit
function getMostSignificantBit(uint256 word) internal pure returns (uint8 mostBitPos) {
assembly {
word := or(word, shr(1, word))
word := or(word, shr(2, word))
word := or(word, shr(4, word))
word := or(word, shr(8, word))
word := or(word, shr(16, word))
word := or(word, shr(32, word))
word := or(word, shr(64, word))
word := or(word, shr(128, word))
word := sub(word, shr(1, word))
}
return (getSingleSignificantBit(word));
}
/// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either
/// to the left (less than or equal to) or right (greater than) of the given tick
/// @param self The mapping in which to compute the next initialized tick
/// @param tick The starting tick
/// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick)
/// @return nextTick The next initialized or uninitialized tick up to 256 ticks away from the current tick
/// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks
function nextTickInTheSameRow(
mapping(int16 => uint256) storage self,
int24 tick,
bool lte
) internal view returns (int24 nextTick, bool initialized) {
if (lte) {
// unpacking not made into a separate function for gas and contract size savings
int16 rowNumber;
uint8 bitNumber;
assembly {
bitNumber := and(tick, 0xFF)
rowNumber := shr(8, tick)
}
uint256 _row = self[rowNumber] << (255 - bitNumber); // all the 1s at or to the right of the current bitNumber
if (_row != 0) {
tick -= int24(255 - getMostSignificantBit(_row));
return (boundTick(tick), true);
} else {
tick -= int24(bitNumber);
return (boundTick(tick), false);
}
} else {
// start from the word of the next tick, since the current tick state doesn't matter
tick += 1;
int16 rowNumber;
uint8 bitNumber;
assembly {
bitNumber := and(tick, 0xFF)
rowNumber := shr(8, tick)
}
// all the 1s at or to the left of the bitNumber
uint256 _row = self[rowNumber] >> (bitNumber);
if (_row != 0) {
tick += int24(getSingleSignificantBit(-_row & _row)); // least significant bit
return (boundTick(tick), true);
} else {
tick += int24(255 - bitNumber);
return (boundTick(tick), false);
}
}
}
function boundTick(int24 tick) private pure returns (int24 boundedTick) {
boundedTick = tick;
if (boundedTick < TickMath.MIN_TICK) {
boundedTick = TickMath.MIN_TICK;
} else if (boundedTick > TickMath.MAX_TICK) {
boundedTick = TickMath.MAX_TICK;
}
}
}
contracts/libraries/TokenDeltaMath.sol
// 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();
}
}
contracts/libraries/TransferHelper.sol
// 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');
}
}
Compiler Settings
{"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"]}},"optimizer":{"runs":0,"enabled":true},"metadata":{"bytecodeHash":"none"},"libraries":{}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"Factory","inputs":[{"type":"address","name":"factory","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address","name":"pool","internalType":"address"}],"name":"deploy","inputs":[{"type":"address","name":"dataStorage","internalType":"address"},{"type":"address","name":"_factory","internalType":"address"},{"type":"address","name":"token0","internalType":"address"},{"type":"address","name":"token1","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"dataStorage","internalType":"address"},{"type":"address","name":"factory","internalType":"address"},{"type":"address","name":"token0","internalType":"address"},{"type":"address","name":"token1","internalType":"address"}],"name":"parameters","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFactory","inputs":[{"type":"address","name":"_factory","internalType":"address"}]}]
Contract Creation Code
0x608060405234801561001057600080fd5b50600580546001600160a01b03191633179055615d2d806100326000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80635bb4780814610046578063890357301461006e578063fd82b73a146100aa575b600080fd5b61006c6004803603602081101561005c57600080fd5b50356001600160a01b0316610104565b005b61007661019a565b604080516001600160a01b039586168152938516602085015291841683830152909216606082015290519081900360800190f35b6100e8600480360360808110156100c057600080fd5b506001600160a01b0381358116916020810135821691604082013581169160600135166101be565b604080516001600160a01b039092168252519081900360200190f35b6005546001600160a01b0316331461011b57600080fd5b6001600160a01b03811661012e57600080fd5b6004546001600160a01b03161561014457600080fd5b6040516001600160a01b038216907f194d5292f3fd797c05e95926ce4b1d4d7f7f10c7dd0c467e55a42d82006bbc6990600090a2600480546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001546002546003546001600160a01b039384169392831692918216911684565b6004546000906001600160a01b031633146101d857600080fd5b604080516080810182526001600160a01b0380881680835287821660208085018290528884168587018190529388166060958601819052600080546001600160a01b03199081169095179055600180548516909317909255600280548416851790556003805490931682179092558451808301939093528285015283518083038501815291909201928390528051910120906102739061029d565b8190604051809103906000f5905080158015610293573d6000803e3d6000fd5b5095945050505050565b615a76806102ab8339019056fe6101006040523480156200001257600080fd5b5033806001600160a01b031663890357306040518163ffffffff1660e01b815260040160806040518083038186803b1580156200004e57600080fd5b505afa15801562000063573d6000803e3d6000fd5b505050506040513d60808110156200007a57600080fd5b5080516020820151604083015160609384015180851b6001600160601b031990811660e05282861b811660c05283861b811660a0529484901b9094166080526002805461ffff60b81b1916601960ba1b1761ffff60c81b1916601960ca1b1790556004805462ffffff60c01b1916600f60c21b1790556001600160a01b0392831694509082169290821691166158a1620001d560003980610a7d5280610bbb5280610eec528061117b528061139a528061162652806116d4528061170e528061287a52806129855280613a95525080610a245280610b1d5280610be15280610e9a52806110b25280611357528061165c528061169e5280612845528061393f525080610ca15280610fb052806118ee5280611a1e528061293f5280612a755280612cab5280613b04525080610db552806117bd5280611dca5280612d3e52806137165280613d4c52806144aa528061457852506158a16000f3fe608060405234801561001057600080fd5b506004361061015f5760003560e01c80630dfe168114610164578063128acb081461018857806317e25b3c146102355780631a68650214610256578063289fe9b01461027a57806329047dfa1461029f578063490e6cbc146102a75780634f1eb3d814610331578063514ea4bf146103b15780636378ae441461041657806370cf754a14610430578063713346941461043857806374eceae6146104d55780637c0112b7146105515780637c1fe0c814610579578063920c34e51461059f5780639d3a5241146105fa578063a34123a71461078b578063aafe29c0146107c5578063c45a015514610897578063c677e3e01461089f578063d0c93a7c146108bf578063d21220a7146108de578063e76c01e4146108e6578063ecdecf421461094a578063f085a61014610952578063f30dba9314610972578063f637731d146109f4578063facb0eb114610a1a575b600080fd5b61016c610a22565b604080516001600160a01b039092168252519081900360200190f35b61021c600480360360a081101561019e57600080fd5b6001600160a01b0382358116926020810135151592604082013592606083013516919081019060a081016080820135600160201b8111156101de57600080fd5b8201836020820111156101f057600080fd5b803590602001918460018302840111600160201b8311171561021157600080fd5b509092509050610a46565b6040805192835260208301919091528051918290030190f35b61023d610c84565b6040805163ffffffff9092168252519081900360200190f35b61025e610c90565b604080516001600160801b039092168252519081900360200190f35b61029d6004803603602081101561029057600080fd5b503563ffffffff16610c9f565b005b61016c610db3565b61029d600480360360808110156102bd57600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b8111156102f357600080fd5b82018360208201111561030557600080fd5b803590602001918460018302840111600160201b8311171561032657600080fd5b509092509050610dd7565b610382600480360360a081101561034757600080fd5b506001600160a01b03813516906020810135600290810b91604081013590910b906001600160801b036060820135811691608001351661124f565b60405180836001600160801b03168152602001826001600160801b031681526020019250505060405180910390f35b6103ce600480360360208110156103c757600080fd5b503561144d565b604080516001600160801b03978816815263ffffffff90961660208701528581019490945260608501929092528416608084015290921660a082015290519081900360c00190f35b61041e611496565b60408051918252519081900360200190f35b61025e61149c565b61021c600480360360c081101561044e57600080fd5b6001600160a01b0382358116926020810135821692604082013515159260608301359260808101359091169181019060c0810160a0820135600160201b81111561049757600080fd5b8201836020820111156104a957600080fd5b803590602001918460018302840111600160201b831117156104ca57600080fd5b5090925090506114ae565b6104f2600480360360208110156104eb57600080fd5b50356117b0565b60408051971515885263ffffffff909616602088015260069490940b868601526001600160a01b0390921660608601526001600160581b0316608085015260020b60a08401526001600160901b031660c0830152519081900360e00190f35b61029d6004803603604081101561056757600080fd5b5060ff81358116916020013516611881565b61029d6004803603602081101561058f57600080fd5b50356001600160a01b0316611a1c565b6105c9600480360360408110156105b557600080fd5b508035600290810b9160200135900b611b0b565b6040805160069490940b84526001600160a01b03909216602084015263ffffffff1682820152519081900360600190f35b6106686004803603602081101561061057600080fd5b810190602081018135600160201b81111561062a57600080fd5b82018360208201111561063c57600080fd5b803590602001918460208302840111600160201b8311171561065d57600080fd5b509092509050611dc2565b6040518080602001806020018060200180602001858103855289818151815260200191508051906020019060200280838360005b838110156106b457818101518382015260200161069c565b50505050905001858103845288818151815260200191508051906020019060200280838360005b838110156106f35781810151838201526020016106db565b50505050905001858103835287818151815260200191508051906020019060200280838360005b8381101561073257818101518382015260200161071a565b50505050905001858103825286818151815260200191508051906020019060200280838360005b83811015610771578181015183820152602001610759565b505050509050019850505050505050505060405180910390f35b61021c600480360360608110156107a157600080fd5b508035600290810b91602081013590910b90604001356001600160801b0316612127565b610870600480360360c08110156107db57600080fd5b6001600160a01b0382358116926020810135909116916040820135600290810b92606081013590910b916001600160801b03608083013516919081019060c0810160a0820135600160201b81111561083257600080fd5b82018360208201111561084457600080fd5b803590602001918460018302840111600160201b8311171561086557600080fd5b509092509050612367565b6040805193845260208401929092526001600160801b031682820152519081900360600190f35b61016c61293d565b61041e600480360360208110156108b557600080fd5b503560010b612961565b6108c7612973565b6040805160029290920b8252519081900360200190f35b61016c612983565b6108ee6129a7565b604080516001600160a01b03909916895260029790970b602089015261ffff95861688880152938516606088015291909316608086015260ff92831660a086015290911660c0840152151560e083015251908190036101000190f35b61041e612a02565b61029d6004803603602081101561096857600080fd5b503560020b612a08565b6109926004803603602081101561098857600080fd5b503560020b612bed565b604080516001600160801b039099168952600f9790970b602089015287870195909552606087019390935260069190910b60808601526001600160a01b031660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b61029d60048036036020811015610a0a57600080fd5b50356001600160a01b0316612c57565b61016c612ec5565b7f000000000000000000000000000000000000000000000000000000000000000081565b600080600080600080610a5a8b8b8b612edb565b949a509298509096509450925090508a15610b0f576000851215610aa657610aa67f00000000000000000000000000000000000000000000000000000000000000008d876000036137ed565b6000610ab061393b565b9050610abe87878b8b6139db565b610ac661393b565b610ad08289613a7b565b1115610b09576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b50610bab565b6000861215610b4657610b467f00000000000000000000000000000000000000000000000000000000000000008d886000036137ed565b6000610b50613a91565b9050610b5e87878b8b6139db565b610b66613a91565b610b708288613a7b565b1115610ba9576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b8015610c0757610c078b610bdf577f0000000000000000000000000000000000000000000000000000000000000000610c01565b7f00000000000000000000000000000000000000000000000000000000000000005b82613b00565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e16913391600080516020615875833981519152919081900360a00190a35050600280546001600160f81b0316600160f81b17905550919890975095505050505050565b60045463ffffffff1681565b6003546001600160801b031681565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610cf857600080fd5b505afa158015610d0c573d6000803e3d6000fd5b505050506040513d6020811015610d2257600080fd5b50516001600160a01b03163314610d3857600080fd5b6201518063ffffffff821611801590610d5c575060045463ffffffff828116911614155b610d6557600080fd5b6004805463ffffffff831663ffffffff19909116811790915560408051918252517fb5e51602371b0e74f991b6e965cd7d32b4b14c7e6ede6d1298037650a0e1405f9181900360200190a150565b7f000000000000000000000000000000000000000000000000000000000000000081565b600254600160f81b900460ff16610e1b576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b031690556003546001600160801b031680610e6c576040805162461bcd60e51b81526020600482015260016024820152601360fa1b604482015290519081900360640190fd5b6064600080610e7961393b565b90508715610ec057610e93888461ffff16620f4240613b99565b9150610ec07f00000000000000000000000000000000000000000000000000000000000000008a8a6137ed565b600080610ecb613a91565b90508815610f1257610ee5898661ffff16620f4240613b99565b9150610f127f00000000000000000000000000000000000000000000000000000000000000008c8b6137ed565b336001600160a01b031663a60b0d3c85848b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015610f9457600080fd5b505af1158015610fa8573d6000803e3d6000fd5b5050505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561100757600080fd5b505afa15801561101b573d6000803e3d6000fd5b505050506040513d602081101561103157600080fd5b50519050600061103f61393b565b90508061104c8688613a7b565b1115611084576040805162461bcd60e51b8152602060048201526002602482015261046360f41b604482015290519081900360640190fd5b84900380156110fe57600254600160e81b900460ff16600081156110d857506103e860ff82168302046110d87f000000000000000000000000000000000000000000000000000000000000000085836137ed565b6110f2818403600160801b8c6001600160801b0316613c0e565b60008054909101905550505b6000611108613a91565b9050806111158587613a7b565b111561114d576040805162461bcd60e51b8152602060048201526002602482015261463160f01b604482015290519081900360640190fd5b83900380156111c757600254600160f01b900460ff16600081156111a157506103e860ff82168302046111a17f000000000000000000000000000000000000000000000000000000000000000086836137ed565b6111bb818403600160801b8d6001600160801b0316613c0e565b60018054909101905550505b8d6001600160a01b0316336001600160a01b03167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a35050600280546001600160f81b0316600160f81b179055505050505050505050505050565b6002546000908190600160f81b900460ff16611298576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316905560006112b4338888613ca4565b60038101549091506001600160801b0380821691600160801b9004811690871682106112e057866112e2565b815b9450806001600160801b0316866001600160801b0316116113035785611305565b805b93506001600160801b0385851716156113c9576003830180546001600160801b0319168684036001600160801b03908116919091178116600160801b87850383160217909155851615611386576113867f00000000000000000000000000000000000000000000000000000000000000008b876001600160801b03166137ed565b6001600160801b038416156113c9576113c97f00000000000000000000000000000000000000000000000000000000000000008b866001600160801b03166137ed565b604080516001600160a01b038c1681526001600160801b038088166020830152861681830152905160028a810b92908c900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a45050600280546001600160f81b0316600160f81b1790555090969095509350505050565b60076020526000908152604090208054600182015460028301546003909301546001600160801b038084169463ffffffff600160801b9586900416949092808316929190041686565b60005481565b6d09745258e83de0d0f4e400fce79990565b6002546000908190600160f81b900460ff166114f7576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b03169055861561157a57600061151661393b565b905061152587600087876139db565b60006115398261153361393b565b90613cc8565b97508713611574576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b506115de565b6000611584613a91565b905061159360008887876139db565b60006115a182611533613a91565b975087136115dc576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b600280546001600160f81b0316600160f81b17905560008080806116038b8b8b612edb565b949a509298509096509450925090508a1561169057600085121561164f5761164f7f00000000000000000000000000000000000000000000000000000000000000008d876000036137ed565b8986121561168b5761168b7f00000000000000000000000000000000000000000000000000000000000000008e6116868d8a613cd8565b6137ed565b6116fe565b60008612156116c7576116c77f00000000000000000000000000000000000000000000000000000000000000008d886000036137ed565b898512156116fe576116fe7f00000000000000000000000000000000000000000000000000000000000000008e6116868d89613cd8565b8015611732576117328b610bdf577f0000000000000000000000000000000000000000000000000000000000000000610c01565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e16913391600080516020615875833981519152919081900360a00190a35050600280546001600160f81b0316600160f81b1790555091999098509650505050505050565b60008060008060008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166374eceae6896040518263ffffffff1660e01b81526004018082815260200191505060e06040518083038186803b15801561181f57600080fd5b505afa158015611833573d6000803e3d6000fd5b505050506040513d60e081101561184957600080fd5b508051602082015160408301516060840151608085015160a086015160c090960151949e939d50919b50995097509195509350915050565b600254600160f81b900460ff166118c5576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b15801561193457600080fd5b505afa158015611948573d6000803e3d6000fd5b505050506040513d602081101561195e57600080fd5b50516001600160a01b0316331461197457600080fd5b60fa60ff83161180159061198c575060fa60ff821611155b61199557600080fd5b6002805460ff60f01b1916600160f01b60ff8481169182029290921760ff60e81b1916600160e81b9286169283021790925560408051918252602082019290925281517f9e22b964b08e25c3aaa72102bb0071c089258fb82d51271a8ddf5c24921356ee929181900390910190a15050600280546001600160f81b0316600160f81b179055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638a2ade586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a7557600080fd5b505afa158015611a89573d6000803e3d6000fd5b505050506040513d6020811015611a9f57600080fd5b50516001600160a01b03163314611ab557600080fd5b60048054600160201b600160c01b031916600160201b6001600160a01b038416908102919091179091556040517f915c5369e6580733735d1c2e30ca20dcaa395697a041033c9f35f80f53525e8490600090a250565b600080808484620d89e9600282900b12611b52576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13611b92576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13611bd4576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b611bdc6157a5565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c4457600080fd5b50611c4d6157a5565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611cb557600080fd5b5060028054600160a01b8104820b91600160d81b90910461ffff16908b810b9083900b1215611d0e5782600001518460000151038360200151856020015103846040015186604001510398509850985050505050611db9565b8960020b8260020b1215611d8d576000611d26613cee565b9050600080611d4e8360008787600360009054906101000a90046001600160801b0316613cf2565b5050915091508560000151876000015183030386602001518860200151830303876040015189604001518603039b509b509b5050505050505050611db9565b836000015183600001510384602001518460200151038560400151856040015103985098509850505050505b50509250925092565b6060806060807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fd31e988611dff613cee565b6002805460035460405160e086901b6001600160e01b031916815263ffffffff851660048201908152600160a01b8404850b9485900b6044830152600160d81b90930461ffff16606482018190526001600160801b039092166084820181905260a06024830190815260a483018e90528e958e9590949390919060c401876020880280828437600081840152601f19601f82011690508083019250505097505050505050505060006040518083038186803b158015611ebd57600080fd5b505afa158015611ed1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526080811015611efa57600080fd5b8101908080516040519392919084600160201b821115611f1957600080fd5b908301906020820185811115611f2e57600080fd5b82518660208202830111600160201b82111715611f4a57600080fd5b82525081516020918201928201910280838360005b83811015611f77578181015183820152602001611f5f565b5050505090500160405260200180516040519392919084600160201b821115611f9f57600080fd5b908301906020820185811115611fb457600080fd5b82518660208202830111600160201b82111715611fd057600080fd5b82525081516020918201928201910280838360005b83811015611ffd578181015183820152602001611fe5565b5050505090500160405260200180516040519392919084600160201b82111561202557600080fd5b90830190602082018581111561203a57600080fd5b82518660208202830111600160201b8211171561205657600080fd5b82525081516020918201928201910280838360005b8381101561208357818101518382015260200161206b565b5050505090500160405260200180516040519392919084600160201b8211156120ab57600080fd5b9083019060208201858111156120c057600080fd5b82518660208202830111600160201b821117156120dc57600080fd5b82525081516020918201928201910280838360005b838110156121095781810151838201526020016120f1565b50505050905001604052505050935093509350935092959194509250565b6002546000908190600160f81b900460ff16612170576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316815585908590620d89e99082900b126121c3576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13612203576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612245576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b600080600061226a338b8b6122628c6001600160801b0316613de2565b600003613df8565b9250925092508160000396508060000395508587176000146122eb57600383015461229e906001600160801b0316886140b7565b60038401546122bd90600160801b90046001600160801b0316886140b7565b6003850180546001600160801b03938416928416600160801b029316929092176001600160801b0319161790555b604080516001600160801b038a16815260208101899052808201889052905160028b810b92908d900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a45050600280546001600160f81b0316600160f81b179055509296919550909350505050565b60025460009081908190600160f81b900460ff166123b2576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316815588908890620d89e99082900b12612405576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13612445576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612487576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b6000886001600160801b0316116124ca576040805162461bcd60e51b8152602060048201526002602482015261125360f21b604482015290519081900360640190fd5b600454600160c01b9004600290810b9081810b908b900b816124e857fe5b078160020b8c60020b816124f857fe5b071760020b15612544576040805162461bcd60e51b81526020600482015260126024820152711d1a58dac81a5cc81b9bdd081cdc1858d95960721b604482015290519081900360640190fd5b5060008061257c8c8c61255f8d6001600160801b0316613de2565b60028054600160a01b810490910b906001600160a01b03166140d3565b50909750955060009150819050861561259a5761259761393b565b91505b85156125ab576125a8613a91565b90505b336001600160a01b0316633dd657c588888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561262d57600080fd5b505af1158015612641573d6000803e3d6000fd5b5050505060008711156126985760008261265961393b565b0392508211612698576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b85156126e8576000816126a9613a91565b03915081116126e8576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b8994508682101561270a576127078a6001600160801b03168389613c0e565b94505b8581101561274a5760006127288b6001600160801b03168389613c0e565b9050856001600160801b0316816001600160801b03161015612748578095505b505b6000856001600160801b031611612791576040805162461bcd60e51b8152602060048083019190915260248201526324a4a61960e11b604482015290519081900360640190fd5b6000806127b18f8f8f6127ac8b6001600160801b0316613de2565b613df8565b9250925050838299508911156127f6576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b82819850881115612836576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b50508682111561286d5761286d7f00000000000000000000000000000000000000000000000000000000000000008f8985036137ed565b858111156128a2576128a27f00000000000000000000000000000000000000000000000000000000000000008f8884036137ed565b8a60020b8c60020b8e6001600160a01b03167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde33898c8c60405180856001600160a01b03168152602001846001600160801b0316815260200183815260200182815260200194505050505060405180910390a45050600280546001600160f81b0316600160f81b17905550929a919950975095505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60066020526000908152604090205481565b600454600160c01b900460020b81565b7f000000000000000000000000000000000000000000000000000000000000000081565b600280546001600160a01b03811691600160a01b8204900b9061ffff600160b81b8204811691600160c81b8104821691600160d81b8204169060ff600160e81b8204811691600160f01b8104821691600160f81b9091041688565b60015481565b600254600160f81b900460ff16612a4c576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b158015612abb57600080fd5b505afa158015612acf573d6000803e3d6000fd5b505050506040513d6020811015612ae557600080fd5b50516001600160a01b03163314612afb57600080fd5b60008160020b138015612b1457506101f4600282900b13155b8015612b325750600454600282810b600160c01b909204810b900b14155b612b7c576040805162461bcd60e51b8152602060048201526016602482015275496e76616c6964206e65775469636b53706163696e6760501b604482015290519081900360640190fd5b60048054600283900b62ffffff8116600160c01b0262ffffff60c01b199092169190911790915560408051918252517f01413b1d5d4c359e9a0daa7909ecda165f6e8c51fe2ff529d74b22a5a7c026459181900360200190a150600280546001600160f81b0316600160f81b179055565b60056020526000908152604090208054600182015460028301546003909301546001600160801b03831693600160801b909304600f0b9290600681900b90600160381b81046001600160a01b031690600160d81b810463ffffffff1690600160f81b900460ff1688565b6002546001600160a01b031615612c9a576040805162461bcd60e51b8152602060048201526002602482015261414960f01b604482015290519081900360640190fd5b6000612ca58261416b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632f8a39dd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d0257600080fd5b505afa158015612d16573d6000803e3d6000fd5b505050506040513d6020811015612d2c57600080fd5b505190506000612d3a613cee565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663475fb80c82856040518363ffffffff1660e01b8152600401808363ffffffff1681526020018260020b815260200192505050600060405180830381600087803b158015612db357600080fd5b505af1158015612dc7573d6000803e3d6000fd5b5050505083600260000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550816002600001601d6101000a81548160ff021916908360ff160217905550816002600001601e6101000a81548160ff021916908360ff16021790555060016002600001601f6101000a81548160ff02191690831515021790555082600260000160146101000a81548162ffffff021916908360020b62ffffff1602179055507f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95848460405180836001600160a01b031681526020018260020b81526020019250505060405180910390a150505050565b600454600160201b90046001600160a01b031681565b6000806000806000806000612eee6157c5565b600280546001600160a01b0381169750600160a01b9004900b94508a612f2157600254600160c81b900461ffff16612f30565b600254600160b81b900461ffff165b61ffff90811661016083015260028054600160d81b81049092166101a08401526001600160f81b038216905560ff600160e81b8204811691600160f01b8104821691600160f81b9091041680612fb3576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b8c612fea576040805162461bcd60e51b8152602060048201526002602482015261415360f01b604482015290519081900360640190fd5b60008d1361014085015260a084018d90526003546001600160801b03600160801b8204811660208701521696508d1561309257886001600160a01b03168c6001600160a01b031610801561304b57506401000276a36001600160a01b038d16115b613082576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60005460e0850152828452613112565b886001600160a01b03168c6001600160a01b03161180156130cf575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038d16105b613106576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60015460e08501528184525b600288810b900b610180850152613127613cee565b600454909550600160201b90046001600160a01b03161561323d57600060048054906101000a90046001600160a01b03166001600160a01b0316637f376059876040518263ffffffff1660e01b8152600401808263ffffffff168152602001915050602060405180830381600087803b1580156131a357600080fd5b505af11580156131b7573d6000803e3d6000fd5b505050506040513d60208110156131cd57600080fd5b5051905060008160028111156131df57fe5b14156131fd5760048054600160201b600160c01b031916905561323b565b600181600281111561320b57fe5b141561321e57600161012086015261323b565b600281600281111561322c57fe5b141561323b5760026101208601525b505b600061325a856101a00151878761018001518b8960200151614456565b9050846101a0015161ffff168161ffff16146132bc5761ffff81166101a0860152600060208601528e156132a457613294868a838b61452a565b5061ffff166101608601526132bc565b6132b0868a838b61452a565b61ffff16610160870152505b505050506132c8615836565b6001600160a01b03871681526132e06006878e61466c565b15156040830152600290810b900b602082018190526132fe90614755565b6001600160a01b039081166060830181905261333c918e918a918e16118215151461332d57836060015161332f565b8c5b888f876101600151614a6f565b60c085015260a08401526080830152610140830151909750156133955761336c8160c00151826080015101614c33565b8b039a5061338b6133808260a00151614c33565b60c084015190613cd8565b60c08301526133cd565b6133a28160a00151614c33565b8b019a506133c76133bc8260c00151836080015101614c33565b60c084015190614c49565b60c08301525b81511561340957815160c08201516000916103e8916133eb91614c5f565b816133f257fe5b60c084018051929091049182900390529490940193505b6001600160801b03851615613440576134348160c00151600160801b876001600160801b0316613c0e565b60e08301805190910190525b80606001516001600160a01b0316876001600160a01b03161415613607578060400151156135e85781608001516134c457613489836000846101800151856101a0015189613cf2565b50506001600160a01b03166060840152600690810b900b6040830152600160808301528b6134b9576000546134bd565b6001545b6101008301525b600082610120015160028111156134d757fe5b1461355f5760048054906101000a90046001600160a01b03166001600160a01b03166301342b1982602001518e6040518363ffffffff1660e01b8152600401808360020b8152602001821515815260200192505050600060405180830381600087803b15801561354657600080fd5b505af115801561355a573d6000803e3d6000fd5b505050505b60008c156135a45761359a82602001518460e0015185610100015186606001518760400151896005614c83909695949392919063ffffffff16565b60000390506135da565b6135d782602001518461010001518560e0015186606001518760400151896005614c83909695949392919063ffffffff16565b90505b6135e48682614d37565b9550505b8b6135f7578060200151613600565b60018160200151035b955061362b565b80516001600160a01b0388811691161461362b576136248761416b565b9550613658565b8a15806136495750896001600160a01b0316876001600160a01b0316145b1561365357613658565b6132c8565b81610140015115158c151514613679578160c001518b8360a0015103613686565b8a8260a00151038260c001515b6101a08401516002805461ffff60d81b1916600160d81b61ffff909316929092029190911762ffffff60a01b1916600160a01b62ffffff8b840b1602176001600160a01b0319166001600160a01b038b81169190911790915560408051631b7297f760e11b81526001600160801b038a16600482015260248101859052604481018490529051939c50919a5087927f0000000000000000000000000000000000000000000000000000000000000000909116916336e52fee916064808301926020929190829003018186803b15801561375e57600080fd5b505afa158015613772573d6000803e3d6000fd5b505050506040513d602081101561378857600080fd5b50516020840151600380546001600160801b03948516929093018416600160801b0293909216929092176001600160801b0319169190911790558b156137d55760e08201516000556137de565b60e08201516001555b50505093975093979195509350565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b602083106138695780518252601f19909201916020918201910161384a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146138cb576040519150601f19603f3d011682016040523d82523d6000602084013e6138d0565b606091505b50915091508180156138fe5750805115806138fe57508080602001905160208110156138fb57600080fd5b50515b613934576040805162461bcd60e51b81526020600482015260026024820152612a2360f11b604482015290519081900360640190fd5b5050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156139aa57600080fd5b505afa1580156139be573d6000803e3d6000fd5b505050506040513d60208110156139d457600080fd5b5051905090565b336001600160a01b0316632c8958f6858585856040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015613a5d57600080fd5b505af1158015613a71573d6000803e3d6000fd5b5050505050505050565b80820182811015613a8b57600080fd5b92915050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156139aa57600080fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613b5b57600080fd5b505afa158015613b6f573d6000803e3d6000fd5b505050506040513d6020811015613b8557600080fd5b50519050613b948382846137ed565b505050565b6000831580613bb457505082820282848281613bb157fe5b04145b15613bd55760008211613bc657600080fd5b81810490829006151501613c07565b613be0848484613c0e565b905060008280613bec57fe5b8486091115613c07576000198110613c0357600080fd5b6001015b9392505050565b60008383028160001985870982811083820303915050808411613c3057600080fd5b80613c4057508290049050613c07565b8385870960008581038616958690049560026003880281188089028203028089028203028089028203028089028203028089028203028089029091030291819003819004600101858411909403939093029190930391909104170290509392505050565b62ffffff9081169116601892831b1790911b17600090815260076020526040902090565b80820382811115613a8b57600080fd5b80820382811315600083121514613a8b57600080fd5b4290565b604080516314c5407960e01b815263ffffffff808816600483015286166024820152600285900b604482015261ffff841660648201526001600160801b038316608482015290516000918291829182916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916314c540799160a4808301926080929190829003018186803b158015613d9257600080fd5b505afa158015613da6573d6000803e3d6000fd5b505050506040513d6080811015613dbc57600080fd5b50805160208201516040830151606090930151919c909b50919950975095505050505050565b80600f81900b8114613df357600080fd5b919050565b60408051606081018252600280546001600160a01b0381168352600160a01b8104820b90910b6020830152600160d81b900461ffff169181019190915260009081908190613e47888888613ca4565b60008054600154929650919080600f89900b15613f0e576000613e68613cee565b9050600080613e988360008a602001518b60400151600360009054906101000a90046001600160801b0316613cf2565b505091509150613ec78e89602001518e8a8a86888a60006005614ded909998979695949392919063ffffffff16565b15613edb5760019450613edb60068f614f87565b6020880151613ef6906005908f908f8b8b87898b6001614ded565b15613f0a5760019350613f0a60068e614f87565b5050505b60208501516000908190613f2a906005908f908f908a8a614fb3565b91509150613f3a8a8c8484615055565b8a600f0b6000146140a65760008b600f0b1215613fb5578315613f835760028d810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b8215613fb55760028c810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b6000613fcc8e8e8e8b602001518c600001516140d3565b919b5099509050600f81900b156140a45760035460408901516001600160801b039091169060009061401f90614000613cee565b60208d01516003548690600160801b90046001600160801b0316614456565b90508061ffff168a6040015161ffff16146140775761404b61403f613cee565b8b60200151838561452a565b50506002805461ffff60d81b1916600160d81b61ffff841602179055600380546001600160801b031690555b614081828f614d37565b600380546001600160801b0319166001600160801b039290921691909117905550505b505b505050505050509450945094915050565b8082016001600160801b038084169082161015613a8b57600080fd5b60008060008760020b8560020b1215614108576141016140f289614755565b6140fb89614755565b88615233565b9250614160565b8660020b8560020b121561414257614123846140fb89614755565b925061413861413189614755565b8588615278565b9150859050614160565b61415d61414e89614755565b61415789614755565b88615278565b91505b955095509592505050565b60006401000276a36001600160a01b038316108015906141a7575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038316105b6141dc576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b600160201b600160c01b03602083901b166001600160801b03811160071b81811c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c9790881196179094179092171790911717176080811061426e57607f810383901c9150614278565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d607f198f0160401b60c09190911c6001603f1b161760c19b909b1c6001603e1b169a909a1760c29990991c6001603d1b169890981760c39790971c6001603c1b169690961760c49590951c6001603b1b169490941760c59390931c6001603a1b169290921760c69190911c600160391b161760c79190911c600160381b161760c89190911c600160371b161760c99190911c600160361b161760ca9190911c600160351b161760cb9190911c600160341b161760cc9190911c600160331b161760cd9190911c600160321b1617693627a301d71055774c8581026f028f6481ab7f045a5af012a19d003aa9198101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b1461444757886001600160a01b031661442b82614755565b6001600160a01b031611156144405781614442565b805b614449565b815b9998505050505050505050565b60408051630eea437960e11b815261ffff8716600482015263ffffffff86166024820152600285900b60448201526001600160801b0380851660648301528316608482015290516000916001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691631dd486f29160a48082019260209290919082900301818787803b1580156144f257600080fd5b505af1158015614506573d6000803e3d6000fd5b505050506040513d602081101561451c57600080fd5b505190505b95945050505050565b6040805163a80b96a160e01b815263ffffffff86166004820152600285900b602482015261ffff841660448201526001600160801b0383166064820152815160009283926001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263a80b96a192608480840193919291829003018186803b1580156145bc57600080fd5b505afa1580156145d0573d6000803e3d6000fd5b505050506040513d60408110156145e657600080fd5b5080516020918201516002805461ffff60c81b1916600160c81b61ffff8085169182029290921761ffff60b81b1916600160b81b92861692830217909255604080519182529481019190915283519295509093507f8a89de70856bccec096661388f305b9a75f5f65cb0d8a0e1e803c39dabedb57f92908290030190a194509492505050565b60008082156146ec57600884901c600181810b900b60009081526020879052604090205460ff868116918282039091161b80156146cd576146ac816152a8565b60ff0360ff16870396506146bf876152eb565b60019450945050505061474d565b8160ff16870396506146de876152eb565b60009450945050505061474d565b6001938401600881901c80860b90950b60009081526020879052604090205490949060ff861690811c801561473957614729818260000316615320565b60ff16870196506146bf876152eb565b8160ff0360ff16870196506146de876152eb565b935093915050565b6000600282810b60171d90818418829003900b620d89e88111156147a4576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b6000600182166147b857600160801b6147ca565b6ffffcb933bd6fad37aa2d162d1a5940015b6001600160881b0316905060028216156147f4576ffff97272373d413259a46990580e213a0260801c5b6004821615614813576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615614832576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615614851576fffcb9843d60f6159c9db58835c9266440260801c5b6020821615614870576fff973b41fa98c081472e6896dfb254c00260801c5b604082161561488f576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156148ae576ffe5dee046a99a2a811c461f1969c30530260801c5b6101008216156148ce576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b6102008216156148ee576ff987a7253ac413176f2b074cf7815e540260801c5b61040082161561490e576ff3392b0822b70005940c7a398e4b70f30260801c5b61080082161561492e576fe7159475a2c29b7443b29c7fa6e889d90260801c5b61100082161561494e576fd097f3bdfd2022b8845ad8f792aa58250260801c5b61200082161561496e576fa9f746462d870fdf8a65dc1f90e061e50260801c5b61400082161561498e576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156149ae576f31be135f97d08fd981231505542fcfa60260801c5b620100008216156149cf576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b620200008216156149ef576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614a0e576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614a2b576b048a170391f7dc42444e8fa20260801c5b60008560020b1315614a46578060001981614a4257fe5b0490505b600160201b810615614a59576001614a5c565b60005b60ff16602082901c019350505050919050565b6000806000806158728a614a8557615434614a89565b6154435b905060008712614b75576000614ab1888861ffff16620f42400362ffffff16620f4240613c0e565b9050614ac28a8c8b8563ffffffff16565b9450848110614aef57899550614ae88561ffff891662ffffff620f424082900316613b99565b9250614b4e565b614afb8b8a838f615452565b9550856001600160a01b03168a6001600160a01b031614614b3157614b25868c8b8563ffffffff16565b94508488039250614b4e565b614b4b8561ffff891662ffffff620f424082900316613b99565b92505b614b6d868c8b8f614b6157615462614b65565b6154715b63ffffffff16565b935050614c25565b6158728b614b8557615462614b89565b6154715b9050614b9a8a8c8b8463ffffffff16565b9350876000039750838810614bb157899550614bf6565b614bbd8b8a8a8f615480565b9550856001600160a01b03168a6001600160a01b031614614bea57614be7868c8b8463ffffffff16565b93505b87841115614bf6578793505b614c05868c8b8563ffffffff16565b9450614c218561ffff891662ffffff620f424082900316613b99565b9250505b509650965096509692505050565b6000600160ff1b8210614c4557600080fd5b5090565b81810182811215600083121514613a8b57600080fd5b6000821580614c7a57505081810281838281614c7757fe5b04145b613a8b57600080fd5b600286810b810b60009081526020899052604090206003810180546001600160a01b03600160381b63ffffffff600160d81b808504821689039091160263ffffffff60d81b199093169290921782810482168903909116909102600160381b600160d81b031990911617600681810b8703900b66ffffffffffffff1666ffffffffffffff199091161790559081018054860390556001810180548703905554600160801b9004600f0b979650505050505050565b60008082600f0b1215614d9c57826001600160801b03168260000384039150816001600160801b031610614d97576040805162461bcd60e51b81526020600482015260026024820152614c5360f01b604482015290519081900360640190fd5b613a8b565b826001600160801b03168284019150816001600160801b03161015613a8b576040805162461bcd60e51b81526020600482015260026024820152614c4160f01b604482015290519081900360640190fd5b600289810b900b600090815260208b9052604081208054600160801b8104600f0b906001600160801b031683614e23828d614d37565b90506d09745258e83de0d0f4e400fce79a6001600160801b03821610614e75576040805162461bcd60e51b81526020600482015260026024820152614c4f60f01b604482015290519081900360640190fd5b85614e9857614e93614e8e600f85810b908f900b614c49565b613de2565b614eac565b614eac614e8e600f85810b908f900b613cd8565b84546001600160801b03838116600f9390930b8116600160801b02918116919091176001600160801b03191682178655901595508216614f7557841594508c60020b8e60020b13614f5d57600184018b9055600284018a9055600384018054600160381b600160d81b031916600160381b6001600160a01b038c16021766ffffffffffffff191666ffffffffffffff60068b900b161763ffffffff60d81b1916600160d81b63ffffffff8a16021790555b6003840180546001600160f81b0316600160f81b1790555b505050509a9950505050505050505050565b600881901c600190810b810b60009081526020939093526040909220805460ff9092169290921b189055565b600285810b810b60009081526020889052604080822087840b80850b84529183209293849391929088900b121561502d578860020b8760020b1261500857816001015486039350816002015485039250615017565b81600101549350816002015492505b6001810154600282015494039390920391615048565b81600101548160010154039350816002015481600201540392505b5050965096945050505050565b83546001600160801b03811690600160801b900463ffffffff16600f85900b6150c0576000826001600160801b0316116150bb576040805162461bcd60e51b815260206004820152600260248201526104e560f41b604482015290519081900360640190fd5b615179565b600085600f0b12156150ff5760045463ffffffff1680156150fd578063ffffffff16826150eb613cee565b0363ffffffff1610156150fd57600080fd5b505b600061510b8387614d37565b9050806000826001600160801b03161161512657600061513f565b600087600f0b13615137578261513f565b61513f613cee565b88546001600160801b039092166001600160801b031963ffffffff909216600160801b0263ffffffff60801b199093169290921716178755505b6001860154600287015460008683146151b057600189018790556151ad8388036001600160801b038716600160801b613c0e565b90505b60008287146151dd5760028a018790556151da8388036001600160801b038816600160801b613c0e565b90505b6001600160801b0382821716156152275760038a0180546001600160801b031981166001600160801b039182168501821617808216600160801b9182900483168501909216021790555b50505050505050505050565b60008082600f0b1215615260576152586152538585856000036000615490565b614c33565b600003615270565b6152706152538585856001615490565b949350505050565b60008082600f0b121561529857615258615253858585600003600061551f565b615270615253858585600161551f565b600181811c909117600281901c17600481901c17600881901c17601081901c17602081901c17604081901c17608081901c179081901c90036000613a8b82615320565b80620d89e719600282900b12156153075750620d89e719613df3565b620d89e8600282900b1315613df35750620d89e8919050565b7f55555555555555555555555555555555555555555555555555555555555555558116156001600160801b0382161560071b176001600160401b03600160801b03600160c01b0382161560061b177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff82161560051b177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff82161560041b177eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff82161560031b177f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f82161560021b177f3333333333333333333333333333333333333333333333333333333333333333919091161560011b1790565b6000615270838584600161551f565b60006152708484846001615490565b6000614521858585856001615590565b60006152708385846000615490565b6000615270848484600061551f565b6000614521858585856000615590565b60006001600160a01b03858503811690851681106154ad57600080fd5b600160601b600160e01b03606085901b16836154ee57866001600160a01b03166154e18383896001600160a01b0316613c0e565b816154e857fe5b04615514565b6155146155058383896001600160a01b0316613b99565b886001600160a01b0316615784565b979650505050505050565b6000846001600160a01b0316846001600160a01b0316101561554057600080fd5b6001600160a01b03858503168261556e5761556981856001600160801b0316600160601b613c0e565b615586565b61558681856001600160801b0316600160601b613b99565b9695505050505050565b600080866001600160a01b0316116155a757600080fd5b6000856001600160801b0316116155bd57600080fd5b81151583151514156156af57836155d5575084614521565b600160601b600160e01b03606086901b168215615663576001600160a01b0387168581029086828161560357fe5b0414156156345781810182811061563257615628838a6001600160a01b031683613b99565b9350505050614521565b505b61565a82615655888b6001600160a01b0316868161564e57fe5b0490613a7b565b615784565b92505050614521565b6001600160a01b0387168581029086828161567a57fe5b041461568557600080fd5b80821161569157600080fd5b61565a6156aa838a6001600160a01b0316848603613b99565b61578f565b81156157165761570f6156aa6001600160a01b038611156156e7576156e286600160601b896001600160801b0316613c0e565b6156ff565b6001600160801b038716606087901b816156fd57fe5b045b6001600160a01b03891690613a7b565b9050614521565b60006001600160a01b038511156157445761573f85600160601b886001600160801b0316613b99565b61575b565b61575b606086901b6001600160801b038816615784565b905080876001600160a01b03161161577257600080fd5b6001600160a01b038716039050614521565b808204910615150190565b806001600160a01b0381168114613df357600080fd5b604080516060810182526000808252602082018190529181019190915290565b604080516101c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052906101208201908152600060208201819052604082018190526060820181905260809091015290565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565bfefec42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a164736f6c6343000706000aa164736f6c6343000706000a
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106100415760003560e01c80635bb4780814610046578063890357301461006e578063fd82b73a146100aa575b600080fd5b61006c6004803603602081101561005c57600080fd5b50356001600160a01b0316610104565b005b61007661019a565b604080516001600160a01b039586168152938516602085015291841683830152909216606082015290519081900360800190f35b6100e8600480360360808110156100c057600080fd5b506001600160a01b0381358116916020810135821691604082013581169160600135166101be565b604080516001600160a01b039092168252519081900360200190f35b6005546001600160a01b0316331461011b57600080fd5b6001600160a01b03811661012e57600080fd5b6004546001600160a01b03161561014457600080fd5b6040516001600160a01b038216907f194d5292f3fd797c05e95926ce4b1d4d7f7f10c7dd0c467e55a42d82006bbc6990600090a2600480546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001546002546003546001600160a01b039384169392831692918216911684565b6004546000906001600160a01b031633146101d857600080fd5b604080516080810182526001600160a01b0380881680835287821660208085018290528884168587018190529388166060958601819052600080546001600160a01b03199081169095179055600180548516909317909255600280548416851790556003805490931682179092558451808301939093528285015283518083038501815291909201928390528051910120906102739061029d565b8190604051809103906000f5905080158015610293573d6000803e3d6000fd5b5095945050505050565b615a76806102ab8339019056fe6101006040523480156200001257600080fd5b5033806001600160a01b031663890357306040518163ffffffff1660e01b815260040160806040518083038186803b1580156200004e57600080fd5b505afa15801562000063573d6000803e3d6000fd5b505050506040513d60808110156200007a57600080fd5b5080516020820151604083015160609384015180851b6001600160601b031990811660e05282861b811660c05283861b811660a0529484901b9094166080526002805461ffff60b81b1916601960ba1b1761ffff60c81b1916601960ca1b1790556004805462ffffff60c01b1916600f60c21b1790556001600160a01b0392831694509082169290821691166158a1620001d560003980610a7d5280610bbb5280610eec528061117b528061139a528061162652806116d4528061170e528061287a52806129855280613a95525080610a245280610b1d5280610be15280610e9a52806110b25280611357528061165c528061169e5280612845528061393f525080610ca15280610fb052806118ee5280611a1e528061293f5280612a755280612cab5280613b04525080610db552806117bd5280611dca5280612d3e52806137165280613d4c52806144aa528061457852506158a16000f3fe608060405234801561001057600080fd5b506004361061015f5760003560e01c80630dfe168114610164578063128acb081461018857806317e25b3c146102355780631a68650214610256578063289fe9b01461027a57806329047dfa1461029f578063490e6cbc146102a75780634f1eb3d814610331578063514ea4bf146103b15780636378ae441461041657806370cf754a14610430578063713346941461043857806374eceae6146104d55780637c0112b7146105515780637c1fe0c814610579578063920c34e51461059f5780639d3a5241146105fa578063a34123a71461078b578063aafe29c0146107c5578063c45a015514610897578063c677e3e01461089f578063d0c93a7c146108bf578063d21220a7146108de578063e76c01e4146108e6578063ecdecf421461094a578063f085a61014610952578063f30dba9314610972578063f637731d146109f4578063facb0eb114610a1a575b600080fd5b61016c610a22565b604080516001600160a01b039092168252519081900360200190f35b61021c600480360360a081101561019e57600080fd5b6001600160a01b0382358116926020810135151592604082013592606083013516919081019060a081016080820135600160201b8111156101de57600080fd5b8201836020820111156101f057600080fd5b803590602001918460018302840111600160201b8311171561021157600080fd5b509092509050610a46565b6040805192835260208301919091528051918290030190f35b61023d610c84565b6040805163ffffffff9092168252519081900360200190f35b61025e610c90565b604080516001600160801b039092168252519081900360200190f35b61029d6004803603602081101561029057600080fd5b503563ffffffff16610c9f565b005b61016c610db3565b61029d600480360360808110156102bd57600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b8111156102f357600080fd5b82018360208201111561030557600080fd5b803590602001918460018302840111600160201b8311171561032657600080fd5b509092509050610dd7565b610382600480360360a081101561034757600080fd5b506001600160a01b03813516906020810135600290810b91604081013590910b906001600160801b036060820135811691608001351661124f565b60405180836001600160801b03168152602001826001600160801b031681526020019250505060405180910390f35b6103ce600480360360208110156103c757600080fd5b503561144d565b604080516001600160801b03978816815263ffffffff90961660208701528581019490945260608501929092528416608084015290921660a082015290519081900360c00190f35b61041e611496565b60408051918252519081900360200190f35b61025e61149c565b61021c600480360360c081101561044e57600080fd5b6001600160a01b0382358116926020810135821692604082013515159260608301359260808101359091169181019060c0810160a0820135600160201b81111561049757600080fd5b8201836020820111156104a957600080fd5b803590602001918460018302840111600160201b831117156104ca57600080fd5b5090925090506114ae565b6104f2600480360360208110156104eb57600080fd5b50356117b0565b60408051971515885263ffffffff909616602088015260069490940b868601526001600160a01b0390921660608601526001600160581b0316608085015260020b60a08401526001600160901b031660c0830152519081900360e00190f35b61029d6004803603604081101561056757600080fd5b5060ff81358116916020013516611881565b61029d6004803603602081101561058f57600080fd5b50356001600160a01b0316611a1c565b6105c9600480360360408110156105b557600080fd5b508035600290810b9160200135900b611b0b565b6040805160069490940b84526001600160a01b03909216602084015263ffffffff1682820152519081900360600190f35b6106686004803603602081101561061057600080fd5b810190602081018135600160201b81111561062a57600080fd5b82018360208201111561063c57600080fd5b803590602001918460208302840111600160201b8311171561065d57600080fd5b509092509050611dc2565b6040518080602001806020018060200180602001858103855289818151815260200191508051906020019060200280838360005b838110156106b457818101518382015260200161069c565b50505050905001858103845288818151815260200191508051906020019060200280838360005b838110156106f35781810151838201526020016106db565b50505050905001858103835287818151815260200191508051906020019060200280838360005b8381101561073257818101518382015260200161071a565b50505050905001858103825286818151815260200191508051906020019060200280838360005b83811015610771578181015183820152602001610759565b505050509050019850505050505050505060405180910390f35b61021c600480360360608110156107a157600080fd5b508035600290810b91602081013590910b90604001356001600160801b0316612127565b610870600480360360c08110156107db57600080fd5b6001600160a01b0382358116926020810135909116916040820135600290810b92606081013590910b916001600160801b03608083013516919081019060c0810160a0820135600160201b81111561083257600080fd5b82018360208201111561084457600080fd5b803590602001918460018302840111600160201b8311171561086557600080fd5b509092509050612367565b6040805193845260208401929092526001600160801b031682820152519081900360600190f35b61016c61293d565b61041e600480360360208110156108b557600080fd5b503560010b612961565b6108c7612973565b6040805160029290920b8252519081900360200190f35b61016c612983565b6108ee6129a7565b604080516001600160a01b03909916895260029790970b602089015261ffff95861688880152938516606088015291909316608086015260ff92831660a086015290911660c0840152151560e083015251908190036101000190f35b61041e612a02565b61029d6004803603602081101561096857600080fd5b503560020b612a08565b6109926004803603602081101561098857600080fd5b503560020b612bed565b604080516001600160801b039099168952600f9790970b602089015287870195909552606087019390935260069190910b60808601526001600160a01b031660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b61029d60048036036020811015610a0a57600080fd5b50356001600160a01b0316612c57565b61016c612ec5565b7f000000000000000000000000000000000000000000000000000000000000000081565b600080600080600080610a5a8b8b8b612edb565b949a509298509096509450925090508a15610b0f576000851215610aa657610aa67f00000000000000000000000000000000000000000000000000000000000000008d876000036137ed565b6000610ab061393b565b9050610abe87878b8b6139db565b610ac661393b565b610ad08289613a7b565b1115610b09576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b50610bab565b6000861215610b4657610b467f00000000000000000000000000000000000000000000000000000000000000008d886000036137ed565b6000610b50613a91565b9050610b5e87878b8b6139db565b610b66613a91565b610b708288613a7b565b1115610ba9576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b8015610c0757610c078b610bdf577f0000000000000000000000000000000000000000000000000000000000000000610c01565b7f00000000000000000000000000000000000000000000000000000000000000005b82613b00565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e16913391600080516020615875833981519152919081900360a00190a35050600280546001600160f81b0316600160f81b17905550919890975095505050505050565b60045463ffffffff1681565b6003546001600160801b031681565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610cf857600080fd5b505afa158015610d0c573d6000803e3d6000fd5b505050506040513d6020811015610d2257600080fd5b50516001600160a01b03163314610d3857600080fd5b6201518063ffffffff821611801590610d5c575060045463ffffffff828116911614155b610d6557600080fd5b6004805463ffffffff831663ffffffff19909116811790915560408051918252517fb5e51602371b0e74f991b6e965cd7d32b4b14c7e6ede6d1298037650a0e1405f9181900360200190a150565b7f000000000000000000000000000000000000000000000000000000000000000081565b600254600160f81b900460ff16610e1b576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b031690556003546001600160801b031680610e6c576040805162461bcd60e51b81526020600482015260016024820152601360fa1b604482015290519081900360640190fd5b6064600080610e7961393b565b90508715610ec057610e93888461ffff16620f4240613b99565b9150610ec07f00000000000000000000000000000000000000000000000000000000000000008a8a6137ed565b600080610ecb613a91565b90508815610f1257610ee5898661ffff16620f4240613b99565b9150610f127f00000000000000000000000000000000000000000000000000000000000000008c8b6137ed565b336001600160a01b031663a60b0d3c85848b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015610f9457600080fd5b505af1158015610fa8573d6000803e3d6000fd5b5050505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561100757600080fd5b505afa15801561101b573d6000803e3d6000fd5b505050506040513d602081101561103157600080fd5b50519050600061103f61393b565b90508061104c8688613a7b565b1115611084576040805162461bcd60e51b8152602060048201526002602482015261046360f41b604482015290519081900360640190fd5b84900380156110fe57600254600160e81b900460ff16600081156110d857506103e860ff82168302046110d87f000000000000000000000000000000000000000000000000000000000000000085836137ed565b6110f2818403600160801b8c6001600160801b0316613c0e565b60008054909101905550505b6000611108613a91565b9050806111158587613a7b565b111561114d576040805162461bcd60e51b8152602060048201526002602482015261463160f01b604482015290519081900360640190fd5b83900380156111c757600254600160f01b900460ff16600081156111a157506103e860ff82168302046111a17f000000000000000000000000000000000000000000000000000000000000000086836137ed565b6111bb818403600160801b8d6001600160801b0316613c0e565b60018054909101905550505b8d6001600160a01b0316336001600160a01b03167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a35050600280546001600160f81b0316600160f81b179055505050505050505050505050565b6002546000908190600160f81b900460ff16611298576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316905560006112b4338888613ca4565b60038101549091506001600160801b0380821691600160801b9004811690871682106112e057866112e2565b815b9450806001600160801b0316866001600160801b0316116113035785611305565b805b93506001600160801b0385851716156113c9576003830180546001600160801b0319168684036001600160801b03908116919091178116600160801b87850383160217909155851615611386576113867f00000000000000000000000000000000000000000000000000000000000000008b876001600160801b03166137ed565b6001600160801b038416156113c9576113c97f00000000000000000000000000000000000000000000000000000000000000008b866001600160801b03166137ed565b604080516001600160a01b038c1681526001600160801b038088166020830152861681830152905160028a810b92908c900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a45050600280546001600160f81b0316600160f81b1790555090969095509350505050565b60076020526000908152604090208054600182015460028301546003909301546001600160801b038084169463ffffffff600160801b9586900416949092808316929190041686565b60005481565b6d09745258e83de0d0f4e400fce79990565b6002546000908190600160f81b900460ff166114f7576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b03169055861561157a57600061151661393b565b905061152587600087876139db565b60006115398261153361393b565b90613cc8565b97508713611574576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b506115de565b6000611584613a91565b905061159360008887876139db565b60006115a182611533613a91565b975087136115dc576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b600280546001600160f81b0316600160f81b17905560008080806116038b8b8b612edb565b949a509298509096509450925090508a1561169057600085121561164f5761164f7f00000000000000000000000000000000000000000000000000000000000000008d876000036137ed565b8986121561168b5761168b7f00000000000000000000000000000000000000000000000000000000000000008e6116868d8a613cd8565b6137ed565b6116fe565b60008612156116c7576116c77f00000000000000000000000000000000000000000000000000000000000000008d886000036137ed565b898512156116fe576116fe7f00000000000000000000000000000000000000000000000000000000000000008e6116868d89613cd8565b8015611732576117328b610bdf577f0000000000000000000000000000000000000000000000000000000000000000610c01565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e16913391600080516020615875833981519152919081900360a00190a35050600280546001600160f81b0316600160f81b1790555091999098509650505050505050565b60008060008060008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166374eceae6896040518263ffffffff1660e01b81526004018082815260200191505060e06040518083038186803b15801561181f57600080fd5b505afa158015611833573d6000803e3d6000fd5b505050506040513d60e081101561184957600080fd5b508051602082015160408301516060840151608085015160a086015160c090960151949e939d50919b50995097509195509350915050565b600254600160f81b900460ff166118c5576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b15801561193457600080fd5b505afa158015611948573d6000803e3d6000fd5b505050506040513d602081101561195e57600080fd5b50516001600160a01b0316331461197457600080fd5b60fa60ff83161180159061198c575060fa60ff821611155b61199557600080fd5b6002805460ff60f01b1916600160f01b60ff8481169182029290921760ff60e81b1916600160e81b9286169283021790925560408051918252602082019290925281517f9e22b964b08e25c3aaa72102bb0071c089258fb82d51271a8ddf5c24921356ee929181900390910190a15050600280546001600160f81b0316600160f81b179055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638a2ade586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a7557600080fd5b505afa158015611a89573d6000803e3d6000fd5b505050506040513d6020811015611a9f57600080fd5b50516001600160a01b03163314611ab557600080fd5b60048054600160201b600160c01b031916600160201b6001600160a01b038416908102919091179091556040517f915c5369e6580733735d1c2e30ca20dcaa395697a041033c9f35f80f53525e8490600090a250565b600080808484620d89e9600282900b12611b52576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13611b92576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13611bd4576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b611bdc6157a5565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c4457600080fd5b50611c4d6157a5565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611cb557600080fd5b5060028054600160a01b8104820b91600160d81b90910461ffff16908b810b9083900b1215611d0e5782600001518460000151038360200151856020015103846040015186604001510398509850985050505050611db9565b8960020b8260020b1215611d8d576000611d26613cee565b9050600080611d4e8360008787600360009054906101000a90046001600160801b0316613cf2565b5050915091508560000151876000015183030386602001518860200151830303876040015189604001518603039b509b509b5050505050505050611db9565b836000015183600001510384602001518460200151038560400151856040015103985098509850505050505b50509250925092565b6060806060807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fd31e988611dff613cee565b6002805460035460405160e086901b6001600160e01b031916815263ffffffff851660048201908152600160a01b8404850b9485900b6044830152600160d81b90930461ffff16606482018190526001600160801b039092166084820181905260a06024830190815260a483018e90528e958e9590949390919060c401876020880280828437600081840152601f19601f82011690508083019250505097505050505050505060006040518083038186803b158015611ebd57600080fd5b505afa158015611ed1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526080811015611efa57600080fd5b8101908080516040519392919084600160201b821115611f1957600080fd5b908301906020820185811115611f2e57600080fd5b82518660208202830111600160201b82111715611f4a57600080fd5b82525081516020918201928201910280838360005b83811015611f77578181015183820152602001611f5f565b5050505090500160405260200180516040519392919084600160201b821115611f9f57600080fd5b908301906020820185811115611fb457600080fd5b82518660208202830111600160201b82111715611fd057600080fd5b82525081516020918201928201910280838360005b83811015611ffd578181015183820152602001611fe5565b5050505090500160405260200180516040519392919084600160201b82111561202557600080fd5b90830190602082018581111561203a57600080fd5b82518660208202830111600160201b8211171561205657600080fd5b82525081516020918201928201910280838360005b8381101561208357818101518382015260200161206b565b5050505090500160405260200180516040519392919084600160201b8211156120ab57600080fd5b9083019060208201858111156120c057600080fd5b82518660208202830111600160201b821117156120dc57600080fd5b82525081516020918201928201910280838360005b838110156121095781810151838201526020016120f1565b50505050905001604052505050935093509350935092959194509250565b6002546000908190600160f81b900460ff16612170576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316815585908590620d89e99082900b126121c3576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13612203576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612245576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b600080600061226a338b8b6122628c6001600160801b0316613de2565b600003613df8565b9250925092508160000396508060000395508587176000146122eb57600383015461229e906001600160801b0316886140b7565b60038401546122bd90600160801b90046001600160801b0316886140b7565b6003850180546001600160801b03938416928416600160801b029316929092176001600160801b0319161790555b604080516001600160801b038a16815260208101899052808201889052905160028b810b92908d900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a45050600280546001600160f81b0316600160f81b179055509296919550909350505050565b60025460009081908190600160f81b900460ff166123b2576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316815588908890620d89e99082900b12612405576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13612445576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612487576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b6000886001600160801b0316116124ca576040805162461bcd60e51b8152602060048201526002602482015261125360f21b604482015290519081900360640190fd5b600454600160c01b9004600290810b9081810b908b900b816124e857fe5b078160020b8c60020b816124f857fe5b071760020b15612544576040805162461bcd60e51b81526020600482015260126024820152711d1a58dac81a5cc81b9bdd081cdc1858d95960721b604482015290519081900360640190fd5b5060008061257c8c8c61255f8d6001600160801b0316613de2565b60028054600160a01b810490910b906001600160a01b03166140d3565b50909750955060009150819050861561259a5761259761393b565b91505b85156125ab576125a8613a91565b90505b336001600160a01b0316633dd657c588888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561262d57600080fd5b505af1158015612641573d6000803e3d6000fd5b5050505060008711156126985760008261265961393b565b0392508211612698576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b85156126e8576000816126a9613a91565b03915081116126e8576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b8994508682101561270a576127078a6001600160801b03168389613c0e565b94505b8581101561274a5760006127288b6001600160801b03168389613c0e565b9050856001600160801b0316816001600160801b03161015612748578095505b505b6000856001600160801b031611612791576040805162461bcd60e51b8152602060048083019190915260248201526324a4a61960e11b604482015290519081900360640190fd5b6000806127b18f8f8f6127ac8b6001600160801b0316613de2565b613df8565b9250925050838299508911156127f6576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b82819850881115612836576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b50508682111561286d5761286d7f00000000000000000000000000000000000000000000000000000000000000008f8985036137ed565b858111156128a2576128a27f00000000000000000000000000000000000000000000000000000000000000008f8884036137ed565b8a60020b8c60020b8e6001600160a01b03167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde33898c8c60405180856001600160a01b03168152602001846001600160801b0316815260200183815260200182815260200194505050505060405180910390a45050600280546001600160f81b0316600160f81b17905550929a919950975095505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60066020526000908152604090205481565b600454600160c01b900460020b81565b7f000000000000000000000000000000000000000000000000000000000000000081565b600280546001600160a01b03811691600160a01b8204900b9061ffff600160b81b8204811691600160c81b8104821691600160d81b8204169060ff600160e81b8204811691600160f01b8104821691600160f81b9091041688565b60015481565b600254600160f81b900460ff16612a4c576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b600280546001600160f81b0316905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b158015612abb57600080fd5b505afa158015612acf573d6000803e3d6000fd5b505050506040513d6020811015612ae557600080fd5b50516001600160a01b03163314612afb57600080fd5b60008160020b138015612b1457506101f4600282900b13155b8015612b325750600454600282810b600160c01b909204810b900b14155b612b7c576040805162461bcd60e51b8152602060048201526016602482015275496e76616c6964206e65775469636b53706163696e6760501b604482015290519081900360640190fd5b60048054600283900b62ffffff8116600160c01b0262ffffff60c01b199092169190911790915560408051918252517f01413b1d5d4c359e9a0daa7909ecda165f6e8c51fe2ff529d74b22a5a7c026459181900360200190a150600280546001600160f81b0316600160f81b179055565b60056020526000908152604090208054600182015460028301546003909301546001600160801b03831693600160801b909304600f0b9290600681900b90600160381b81046001600160a01b031690600160d81b810463ffffffff1690600160f81b900460ff1688565b6002546001600160a01b031615612c9a576040805162461bcd60e51b8152602060048201526002602482015261414960f01b604482015290519081900360640190fd5b6000612ca58261416b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632f8a39dd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d0257600080fd5b505afa158015612d16573d6000803e3d6000fd5b505050506040513d6020811015612d2c57600080fd5b505190506000612d3a613cee565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663475fb80c82856040518363ffffffff1660e01b8152600401808363ffffffff1681526020018260020b815260200192505050600060405180830381600087803b158015612db357600080fd5b505af1158015612dc7573d6000803e3d6000fd5b5050505083600260000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550816002600001601d6101000a81548160ff021916908360ff160217905550816002600001601e6101000a81548160ff021916908360ff16021790555060016002600001601f6101000a81548160ff02191690831515021790555082600260000160146101000a81548162ffffff021916908360020b62ffffff1602179055507f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95848460405180836001600160a01b031681526020018260020b81526020019250505060405180910390a150505050565b600454600160201b90046001600160a01b031681565b6000806000806000806000612eee6157c5565b600280546001600160a01b0381169750600160a01b9004900b94508a612f2157600254600160c81b900461ffff16612f30565b600254600160b81b900461ffff165b61ffff90811661016083015260028054600160d81b81049092166101a08401526001600160f81b038216905560ff600160e81b8204811691600160f01b8104821691600160f81b9091041680612fb3576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b8c612fea576040805162461bcd60e51b8152602060048201526002602482015261415360f01b604482015290519081900360640190fd5b60008d1361014085015260a084018d90526003546001600160801b03600160801b8204811660208701521696508d1561309257886001600160a01b03168c6001600160a01b031610801561304b57506401000276a36001600160a01b038d16115b613082576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60005460e0850152828452613112565b886001600160a01b03168c6001600160a01b03161180156130cf575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038d16105b613106576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60015460e08501528184525b600288810b900b610180850152613127613cee565b600454909550600160201b90046001600160a01b03161561323d57600060048054906101000a90046001600160a01b03166001600160a01b0316637f376059876040518263ffffffff1660e01b8152600401808263ffffffff168152602001915050602060405180830381600087803b1580156131a357600080fd5b505af11580156131b7573d6000803e3d6000fd5b505050506040513d60208110156131cd57600080fd5b5051905060008160028111156131df57fe5b14156131fd5760048054600160201b600160c01b031916905561323b565b600181600281111561320b57fe5b141561321e57600161012086015261323b565b600281600281111561322c57fe5b141561323b5760026101208601525b505b600061325a856101a00151878761018001518b8960200151614456565b9050846101a0015161ffff168161ffff16146132bc5761ffff81166101a0860152600060208601528e156132a457613294868a838b61452a565b5061ffff166101608601526132bc565b6132b0868a838b61452a565b61ffff16610160870152505b505050506132c8615836565b6001600160a01b03871681526132e06006878e61466c565b15156040830152600290810b900b602082018190526132fe90614755565b6001600160a01b039081166060830181905261333c918e918a918e16118215151461332d57836060015161332f565b8c5b888f876101600151614a6f565b60c085015260a08401526080830152610140830151909750156133955761336c8160c00151826080015101614c33565b8b039a5061338b6133808260a00151614c33565b60c084015190613cd8565b60c08301526133cd565b6133a28160a00151614c33565b8b019a506133c76133bc8260c00151836080015101614c33565b60c084015190614c49565b60c08301525b81511561340957815160c08201516000916103e8916133eb91614c5f565b816133f257fe5b60c084018051929091049182900390529490940193505b6001600160801b03851615613440576134348160c00151600160801b876001600160801b0316613c0e565b60e08301805190910190525b80606001516001600160a01b0316876001600160a01b03161415613607578060400151156135e85781608001516134c457613489836000846101800151856101a0015189613cf2565b50506001600160a01b03166060840152600690810b900b6040830152600160808301528b6134b9576000546134bd565b6001545b6101008301525b600082610120015160028111156134d757fe5b1461355f5760048054906101000a90046001600160a01b03166001600160a01b03166301342b1982602001518e6040518363ffffffff1660e01b8152600401808360020b8152602001821515815260200192505050600060405180830381600087803b15801561354657600080fd5b505af115801561355a573d6000803e3d6000fd5b505050505b60008c156135a45761359a82602001518460e0015185610100015186606001518760400151896005614c83909695949392919063ffffffff16565b60000390506135da565b6135d782602001518461010001518560e0015186606001518760400151896005614c83909695949392919063ffffffff16565b90505b6135e48682614d37565b9550505b8b6135f7578060200151613600565b60018160200151035b955061362b565b80516001600160a01b0388811691161461362b576136248761416b565b9550613658565b8a15806136495750896001600160a01b0316876001600160a01b0316145b1561365357613658565b6132c8565b81610140015115158c151514613679578160c001518b8360a0015103613686565b8a8260a00151038260c001515b6101a08401516002805461ffff60d81b1916600160d81b61ffff909316929092029190911762ffffff60a01b1916600160a01b62ffffff8b840b1602176001600160a01b0319166001600160a01b038b81169190911790915560408051631b7297f760e11b81526001600160801b038a16600482015260248101859052604481018490529051939c50919a5087927f0000000000000000000000000000000000000000000000000000000000000000909116916336e52fee916064808301926020929190829003018186803b15801561375e57600080fd5b505afa158015613772573d6000803e3d6000fd5b505050506040513d602081101561378857600080fd5b50516020840151600380546001600160801b03948516929093018416600160801b0293909216929092176001600160801b0319169190911790558b156137d55760e08201516000556137de565b60e08201516001555b50505093975093979195509350565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b602083106138695780518252601f19909201916020918201910161384a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146138cb576040519150601f19603f3d011682016040523d82523d6000602084013e6138d0565b606091505b50915091508180156138fe5750805115806138fe57508080602001905160208110156138fb57600080fd5b50515b613934576040805162461bcd60e51b81526020600482015260026024820152612a2360f11b604482015290519081900360640190fd5b5050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156139aa57600080fd5b505afa1580156139be573d6000803e3d6000fd5b505050506040513d60208110156139d457600080fd5b5051905090565b336001600160a01b0316632c8958f6858585856040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015613a5d57600080fd5b505af1158015613a71573d6000803e3d6000fd5b5050505050505050565b80820182811015613a8b57600080fd5b92915050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156139aa57600080fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613b5b57600080fd5b505afa158015613b6f573d6000803e3d6000fd5b505050506040513d6020811015613b8557600080fd5b50519050613b948382846137ed565b505050565b6000831580613bb457505082820282848281613bb157fe5b04145b15613bd55760008211613bc657600080fd5b81810490829006151501613c07565b613be0848484613c0e565b905060008280613bec57fe5b8486091115613c07576000198110613c0357600080fd5b6001015b9392505050565b60008383028160001985870982811083820303915050808411613c3057600080fd5b80613c4057508290049050613c07565b8385870960008581038616958690049560026003880281188089028203028089028203028089028203028089028203028089028203028089029091030291819003819004600101858411909403939093029190930391909104170290509392505050565b62ffffff9081169116601892831b1790911b17600090815260076020526040902090565b80820382811115613a8b57600080fd5b80820382811315600083121514613a8b57600080fd5b4290565b604080516314c5407960e01b815263ffffffff808816600483015286166024820152600285900b604482015261ffff841660648201526001600160801b038316608482015290516000918291829182916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916314c540799160a4808301926080929190829003018186803b158015613d9257600080fd5b505afa158015613da6573d6000803e3d6000fd5b505050506040513d6080811015613dbc57600080fd5b50805160208201516040830151606090930151919c909b50919950975095505050505050565b80600f81900b8114613df357600080fd5b919050565b60408051606081018252600280546001600160a01b0381168352600160a01b8104820b90910b6020830152600160d81b900461ffff169181019190915260009081908190613e47888888613ca4565b60008054600154929650919080600f89900b15613f0e576000613e68613cee565b9050600080613e988360008a602001518b60400151600360009054906101000a90046001600160801b0316613cf2565b505091509150613ec78e89602001518e8a8a86888a60006005614ded909998979695949392919063ffffffff16565b15613edb5760019450613edb60068f614f87565b6020880151613ef6906005908f908f8b8b87898b6001614ded565b15613f0a5760019350613f0a60068e614f87565b5050505b60208501516000908190613f2a906005908f908f908a8a614fb3565b91509150613f3a8a8c8484615055565b8a600f0b6000146140a65760008b600f0b1215613fb5578315613f835760028d810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b8215613fb55760028c810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b6000613fcc8e8e8e8b602001518c600001516140d3565b919b5099509050600f81900b156140a45760035460408901516001600160801b039091169060009061401f90614000613cee565b60208d01516003548690600160801b90046001600160801b0316614456565b90508061ffff168a6040015161ffff16146140775761404b61403f613cee565b8b60200151838561452a565b50506002805461ffff60d81b1916600160d81b61ffff841602179055600380546001600160801b031690555b614081828f614d37565b600380546001600160801b0319166001600160801b039290921691909117905550505b505b505050505050509450945094915050565b8082016001600160801b038084169082161015613a8b57600080fd5b60008060008760020b8560020b1215614108576141016140f289614755565b6140fb89614755565b88615233565b9250614160565b8660020b8560020b121561414257614123846140fb89614755565b925061413861413189614755565b8588615278565b9150859050614160565b61415d61414e89614755565b61415789614755565b88615278565b91505b955095509592505050565b60006401000276a36001600160a01b038316108015906141a7575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038316105b6141dc576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b600160201b600160c01b03602083901b166001600160801b03811160071b81811c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c9790881196179094179092171790911717176080811061426e57607f810383901c9150614278565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d607f198f0160401b60c09190911c6001603f1b161760c19b909b1c6001603e1b169a909a1760c29990991c6001603d1b169890981760c39790971c6001603c1b169690961760c49590951c6001603b1b169490941760c59390931c6001603a1b169290921760c69190911c600160391b161760c79190911c600160381b161760c89190911c600160371b161760c99190911c600160361b161760ca9190911c600160351b161760cb9190911c600160341b161760cc9190911c600160331b161760cd9190911c600160321b1617693627a301d71055774c8581026f028f6481ab7f045a5af012a19d003aa9198101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b1461444757886001600160a01b031661442b82614755565b6001600160a01b031611156144405781614442565b805b614449565b815b9998505050505050505050565b60408051630eea437960e11b815261ffff8716600482015263ffffffff86166024820152600285900b60448201526001600160801b0380851660648301528316608482015290516000916001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691631dd486f29160a48082019260209290919082900301818787803b1580156144f257600080fd5b505af1158015614506573d6000803e3d6000fd5b505050506040513d602081101561451c57600080fd5b505190505b95945050505050565b6040805163a80b96a160e01b815263ffffffff86166004820152600285900b602482015261ffff841660448201526001600160801b0383166064820152815160009283926001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263a80b96a192608480840193919291829003018186803b1580156145bc57600080fd5b505afa1580156145d0573d6000803e3d6000fd5b505050506040513d60408110156145e657600080fd5b5080516020918201516002805461ffff60c81b1916600160c81b61ffff8085169182029290921761ffff60b81b1916600160b81b92861692830217909255604080519182529481019190915283519295509093507f8a89de70856bccec096661388f305b9a75f5f65cb0d8a0e1e803c39dabedb57f92908290030190a194509492505050565b60008082156146ec57600884901c600181810b900b60009081526020879052604090205460ff868116918282039091161b80156146cd576146ac816152a8565b60ff0360ff16870396506146bf876152eb565b60019450945050505061474d565b8160ff16870396506146de876152eb565b60009450945050505061474d565b6001938401600881901c80860b90950b60009081526020879052604090205490949060ff861690811c801561473957614729818260000316615320565b60ff16870196506146bf876152eb565b8160ff0360ff16870196506146de876152eb565b935093915050565b6000600282810b60171d90818418829003900b620d89e88111156147a4576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b6000600182166147b857600160801b6147ca565b6ffffcb933bd6fad37aa2d162d1a5940015b6001600160881b0316905060028216156147f4576ffff97272373d413259a46990580e213a0260801c5b6004821615614813576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615614832576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615614851576fffcb9843d60f6159c9db58835c9266440260801c5b6020821615614870576fff973b41fa98c081472e6896dfb254c00260801c5b604082161561488f576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156148ae576ffe5dee046a99a2a811c461f1969c30530260801c5b6101008216156148ce576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b6102008216156148ee576ff987a7253ac413176f2b074cf7815e540260801c5b61040082161561490e576ff3392b0822b70005940c7a398e4b70f30260801c5b61080082161561492e576fe7159475a2c29b7443b29c7fa6e889d90260801c5b61100082161561494e576fd097f3bdfd2022b8845ad8f792aa58250260801c5b61200082161561496e576fa9f746462d870fdf8a65dc1f90e061e50260801c5b61400082161561498e576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156149ae576f31be135f97d08fd981231505542fcfa60260801c5b620100008216156149cf576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b620200008216156149ef576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614a0e576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614a2b576b048a170391f7dc42444e8fa20260801c5b60008560020b1315614a46578060001981614a4257fe5b0490505b600160201b810615614a59576001614a5c565b60005b60ff16602082901c019350505050919050565b6000806000806158728a614a8557615434614a89565b6154435b905060008712614b75576000614ab1888861ffff16620f42400362ffffff16620f4240613c0e565b9050614ac28a8c8b8563ffffffff16565b9450848110614aef57899550614ae88561ffff891662ffffff620f424082900316613b99565b9250614b4e565b614afb8b8a838f615452565b9550856001600160a01b03168a6001600160a01b031614614b3157614b25868c8b8563ffffffff16565b94508488039250614b4e565b614b4b8561ffff891662ffffff620f424082900316613b99565b92505b614b6d868c8b8f614b6157615462614b65565b6154715b63ffffffff16565b935050614c25565b6158728b614b8557615462614b89565b6154715b9050614b9a8a8c8b8463ffffffff16565b9350876000039750838810614bb157899550614bf6565b614bbd8b8a8a8f615480565b9550856001600160a01b03168a6001600160a01b031614614bea57614be7868c8b8463ffffffff16565b93505b87841115614bf6578793505b614c05868c8b8563ffffffff16565b9450614c218561ffff891662ffffff620f424082900316613b99565b9250505b509650965096509692505050565b6000600160ff1b8210614c4557600080fd5b5090565b81810182811215600083121514613a8b57600080fd5b6000821580614c7a57505081810281838281614c7757fe5b04145b613a8b57600080fd5b600286810b810b60009081526020899052604090206003810180546001600160a01b03600160381b63ffffffff600160d81b808504821689039091160263ffffffff60d81b199093169290921782810482168903909116909102600160381b600160d81b031990911617600681810b8703900b66ffffffffffffff1666ffffffffffffff199091161790559081018054860390556001810180548703905554600160801b9004600f0b979650505050505050565b60008082600f0b1215614d9c57826001600160801b03168260000384039150816001600160801b031610614d97576040805162461bcd60e51b81526020600482015260026024820152614c5360f01b604482015290519081900360640190fd5b613a8b565b826001600160801b03168284019150816001600160801b03161015613a8b576040805162461bcd60e51b81526020600482015260026024820152614c4160f01b604482015290519081900360640190fd5b600289810b900b600090815260208b9052604081208054600160801b8104600f0b906001600160801b031683614e23828d614d37565b90506d09745258e83de0d0f4e400fce79a6001600160801b03821610614e75576040805162461bcd60e51b81526020600482015260026024820152614c4f60f01b604482015290519081900360640190fd5b85614e9857614e93614e8e600f85810b908f900b614c49565b613de2565b614eac565b614eac614e8e600f85810b908f900b613cd8565b84546001600160801b03838116600f9390930b8116600160801b02918116919091176001600160801b03191682178655901595508216614f7557841594508c60020b8e60020b13614f5d57600184018b9055600284018a9055600384018054600160381b600160d81b031916600160381b6001600160a01b038c16021766ffffffffffffff191666ffffffffffffff60068b900b161763ffffffff60d81b1916600160d81b63ffffffff8a16021790555b6003840180546001600160f81b0316600160f81b1790555b505050509a9950505050505050505050565b600881901c600190810b810b60009081526020939093526040909220805460ff9092169290921b189055565b600285810b810b60009081526020889052604080822087840b80850b84529183209293849391929088900b121561502d578860020b8760020b1261500857816001015486039350816002015485039250615017565b81600101549350816002015492505b6001810154600282015494039390920391615048565b81600101548160010154039350816002015481600201540392505b5050965096945050505050565b83546001600160801b03811690600160801b900463ffffffff16600f85900b6150c0576000826001600160801b0316116150bb576040805162461bcd60e51b815260206004820152600260248201526104e560f41b604482015290519081900360640190fd5b615179565b600085600f0b12156150ff5760045463ffffffff1680156150fd578063ffffffff16826150eb613cee565b0363ffffffff1610156150fd57600080fd5b505b600061510b8387614d37565b9050806000826001600160801b03161161512657600061513f565b600087600f0b13615137578261513f565b61513f613cee565b88546001600160801b039092166001600160801b031963ffffffff909216600160801b0263ffffffff60801b199093169290921716178755505b6001860154600287015460008683146151b057600189018790556151ad8388036001600160801b038716600160801b613c0e565b90505b60008287146151dd5760028a018790556151da8388036001600160801b038816600160801b613c0e565b90505b6001600160801b0382821716156152275760038a0180546001600160801b031981166001600160801b039182168501821617808216600160801b9182900483168501909216021790555b50505050505050505050565b60008082600f0b1215615260576152586152538585856000036000615490565b614c33565b600003615270565b6152706152538585856001615490565b949350505050565b60008082600f0b121561529857615258615253858585600003600061551f565b615270615253858585600161551f565b600181811c909117600281901c17600481901c17600881901c17601081901c17602081901c17604081901c17608081901c179081901c90036000613a8b82615320565b80620d89e719600282900b12156153075750620d89e719613df3565b620d89e8600282900b1315613df35750620d89e8919050565b7f55555555555555555555555555555555555555555555555555555555555555558116156001600160801b0382161560071b176001600160401b03600160801b03600160c01b0382161560061b177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff82161560051b177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff82161560041b177eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff82161560031b177f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f82161560021b177f3333333333333333333333333333333333333333333333333333333333333333919091161560011b1790565b6000615270838584600161551f565b60006152708484846001615490565b6000614521858585856001615590565b60006152708385846000615490565b6000615270848484600061551f565b6000614521858585856000615590565b60006001600160a01b03858503811690851681106154ad57600080fd5b600160601b600160e01b03606085901b16836154ee57866001600160a01b03166154e18383896001600160a01b0316613c0e565b816154e857fe5b04615514565b6155146155058383896001600160a01b0316613b99565b886001600160a01b0316615784565b979650505050505050565b6000846001600160a01b0316846001600160a01b0316101561554057600080fd5b6001600160a01b03858503168261556e5761556981856001600160801b0316600160601b613c0e565b615586565b61558681856001600160801b0316600160601b613b99565b9695505050505050565b600080866001600160a01b0316116155a757600080fd5b6000856001600160801b0316116155bd57600080fd5b81151583151514156156af57836155d5575084614521565b600160601b600160e01b03606086901b168215615663576001600160a01b0387168581029086828161560357fe5b0414156156345781810182811061563257615628838a6001600160a01b031683613b99565b9350505050614521565b505b61565a82615655888b6001600160a01b0316868161564e57fe5b0490613a7b565b615784565b92505050614521565b6001600160a01b0387168581029086828161567a57fe5b041461568557600080fd5b80821161569157600080fd5b61565a6156aa838a6001600160a01b0316848603613b99565b61578f565b81156157165761570f6156aa6001600160a01b038611156156e7576156e286600160601b896001600160801b0316613c0e565b6156ff565b6001600160801b038716606087901b816156fd57fe5b045b6001600160a01b03891690613a7b565b9050614521565b60006001600160a01b038511156157445761573f85600160601b886001600160801b0316613b99565b61575b565b61575b606086901b6001600160801b038816615784565b905080876001600160a01b03161161577257600080fd5b6001600160a01b038716039050614521565b808204910615150190565b806001600160a01b0381168114613df357600080fd5b604080516060810182526000808252602082018190529181019190915290565b604080516101c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052906101208201908152600060208201819052604082018190526060820181905260809091015290565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565bfefec42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a164736f6c6343000706000aa164736f6c6343000706000a