Polygon Sponsored slots available. Book your slot here!
Source Code
Latest 6 from a total of 6 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Transfer | 79762248 | 53 days ago | IN | 1 POL | 0.00091877 | ||||
| Increase Observa... | 71487033 | 255 days ago | IN | 0 POL | 0.00065706 | ||||
| Increase Observa... | 71487013 | 255 days ago | IN | 0 POL | 0.00061325 | ||||
| Transfer | 62672077 | 476 days ago | IN | 0.07912147 POL | 0.00086879 | ||||
| Swap | 45729209 | 908 days ago | IN | 0 POL | 0.00243605 | ||||
| Set Community Fe... | 34790411 | 1187 days ago | IN | 0 POL | 0.01243353 |
Latest 1 internal transaction
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 34466889 | 1195 days ago | Contract Creation | 0 POL |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
AlgebraPool
Compiler Version
v0.7.6+commit.7338295f
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './interfaces/IAlgebraPool.sol';
import './interfaces/IDataStorageOperator.sol';
import './interfaces/IAlgebraVirtualPool.sol';
import './base/PoolState.sol';
import './base/PoolImmutables.sol';
import './libraries/TokenDeltaMath.sol';
import './libraries/PriceMovementMath.sol';
import './libraries/TickManager.sol';
import './libraries/TickTable.sol';
import './libraries/LowGasSafeMath.sol';
import './libraries/SafeCast.sol';
import './libraries/FullMath.sol';
import './libraries/Constants.sol';
import './libraries/TransferHelper.sol';
import './libraries/TickMath.sol';
import './libraries/LiquidityMath.sol';
import './interfaces/IAlgebraPoolDeployer.sol';
import './interfaces/IAlgebraFactory.sol';
import './interfaces/IERC20Minimal.sol';
import './interfaces/callback/IAlgebraMintCallback.sol';
import './interfaces/callback/IAlgebraSwapCallback.sol';
import './interfaces/callback/IAlgebraFlashCallback.sol';
contract AlgebraPool is PoolState, PoolImmutables, IAlgebraPool {
using LowGasSafeMath for uint256;
using LowGasSafeMath for int256;
using LowGasSafeMath for uint128;
using SafeCast for uint256;
using SafeCast for int256;
using TickTable for mapping(int16 => uint256);
using TickManager for mapping(int24 => TickManager.Tick);
struct Position {
uint128 liquidity; // The amount of liquidity concentrated in the range
uint32 lastLiquidityAddTimestamp; // Timestamp of last adding of liquidity
uint256 innerFeeGrowth0Token; // The last updated fee growth per unit of liquidity
uint256 innerFeeGrowth1Token;
uint128 fees0; // The amount of token0 owed to a LP
uint128 fees1; // The amount of token1 owed to a LP
}
/// @inheritdoc IAlgebraPoolState
mapping(bytes32 => Position) public override positions;
/// @dev Restricts everyone calling a function except factory owner
modifier onlyFactoryOwner() {
require(msg.sender == IAlgebraFactory(factory).owner());
_;
}
modifier onlyValidTicks(int24 bottomTick, int24 topTick) {
require(topTick < TickMath.MAX_TICK + 1, 'TUM');
require(topTick > bottomTick, 'TLU');
require(bottomTick > TickMath.MIN_TICK - 1, 'TLM');
_;
}
constructor() PoolImmutables(msg.sender) {
globalState.fee = Constants.BASE_FEE;
}
function balanceToken0() private view returns (uint256) {
return IERC20Minimal(token0).balanceOf(address(this));
}
function balanceToken1() private view returns (uint256) {
return IERC20Minimal(token1).balanceOf(address(this));
}
/// @inheritdoc IAlgebraPoolState
function timepoints(uint256 index)
external
view
override
returns (
bool initialized,
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint88 volatilityCumulative,
int24 averageTick,
uint144 volumePerLiquidityCumulative
)
{
return IDataStorageOperator(dataStorageOperator).timepoints(index);
}
struct Cumulatives {
int56 tickCumulative;
uint160 outerSecondPerLiquidity;
uint32 outerSecondsSpent;
}
/// @inheritdoc IAlgebraPoolDerivedState
function getInnerCumulatives(int24 bottomTick, int24 topTick)
external
view
override
onlyValidTicks(bottomTick, topTick)
returns (
int56 innerTickCumulative,
uint160 innerSecondsSpentPerLiquidity,
uint32 innerSecondsSpent
)
{
Cumulatives memory lower;
{
TickManager.Tick storage _lower = ticks[bottomTick];
(lower.tickCumulative, lower.outerSecondPerLiquidity, lower.outerSecondsSpent) = (
_lower.outerTickCumulative,
_lower.outerSecondsPerLiquidity,
_lower.outerSecondsSpent
);
require(_lower.initialized);
}
Cumulatives memory upper;
{
TickManager.Tick storage _upper = ticks[topTick];
(upper.tickCumulative, upper.outerSecondPerLiquidity, upper.outerSecondsSpent) = (
_upper.outerTickCumulative,
_upper.outerSecondsPerLiquidity,
_upper.outerSecondsSpent
);
require(_upper.initialized);
}
(int24 currentTick, uint16 currentTimepointIndex) = (globalState.tick, globalState.timepointIndex);
if (currentTick < bottomTick) {
return (
lower.tickCumulative - upper.tickCumulative,
lower.outerSecondPerLiquidity - upper.outerSecondPerLiquidity,
lower.outerSecondsSpent - upper.outerSecondsSpent
);
}
if (currentTick < topTick) {
uint32 globalTime = _blockTimestamp();
(int56 globalTickCumulative, uint160 globalSecondsPerLiquidityCumulative, , ) = _getSingleTimepoint(
globalTime,
0,
currentTick,
currentTimepointIndex,
liquidity
);
return (
globalTickCumulative - lower.tickCumulative - upper.tickCumulative,
globalSecondsPerLiquidityCumulative - lower.outerSecondPerLiquidity - upper.outerSecondPerLiquidity,
globalTime - lower.outerSecondsSpent - upper.outerSecondsSpent
);
}
return (
upper.tickCumulative - lower.tickCumulative,
upper.outerSecondPerLiquidity - lower.outerSecondPerLiquidity,
upper.outerSecondsSpent - lower.outerSecondsSpent
);
}
/// @inheritdoc IAlgebraPoolDerivedState
function getTimepoints(uint32[] calldata secondsAgos)
external
view
override
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
)
{
return
IDataStorageOperator(dataStorageOperator).getTimepoints(
_blockTimestamp(),
secondsAgos,
globalState.tick,
globalState.timepointIndex,
liquidity
);
}
/// @inheritdoc IAlgebraPoolActions
function initialize(uint160 initialPrice) external override {
require(globalState.price == 0, 'AI');
// getTickAtSqrtRatio checks validity of initialPrice inside
int24 tick = TickMath.getTickAtSqrtRatio(initialPrice);
uint32 timestamp = _blockTimestamp();
IDataStorageOperator(dataStorageOperator).initialize(timestamp, tick);
globalState.price = initialPrice;
globalState.unlocked = true;
globalState.tick = tick;
emit Initialize(initialPrice, tick);
}
/**
* @notice Increases amounts of tokens owed to owner of the position
* @param _position The position object to operate with
* @param liquidityDelta The amount on which to increase\decrease the liquidity
* @param innerFeeGrowth0Token Total fee token0 fee growth per 1/liquidity between position's lower and upper ticks
* @param innerFeeGrowth1Token Total fee token1 fee growth per 1/liquidity between position's lower and upper ticks
*/
function _recalculatePosition(
Position storage _position,
int128 liquidityDelta,
uint256 innerFeeGrowth0Token,
uint256 innerFeeGrowth1Token
) internal {
(uint128 currentLiquidity, uint32 lastLiquidityAddTimestamp) = (_position.liquidity, _position.lastLiquidityAddTimestamp);
if (liquidityDelta == 0) {
require(currentLiquidity > 0, 'NP'); // Do not recalculate the empty ranges
} else {
if (liquidityDelta < 0) {
uint32 _liquidityCooldown = liquidityCooldown;
if (_liquidityCooldown > 0) {
require((_blockTimestamp() - lastLiquidityAddTimestamp) >= _liquidityCooldown);
}
}
// change position liquidity
uint128 liquidityNext = LiquidityMath.addDelta(currentLiquidity, liquidityDelta);
(_position.liquidity, _position.lastLiquidityAddTimestamp) = (
liquidityNext,
liquidityNext > 0 ? (liquidityDelta > 0 ? _blockTimestamp() : lastLiquidityAddTimestamp) : 0
);
}
// update the position
uint256 _innerFeeGrowth0Token = _position.innerFeeGrowth0Token;
uint256 _innerFeeGrowth1Token = _position.innerFeeGrowth1Token;
uint128 fees0;
if (innerFeeGrowth0Token != _innerFeeGrowth0Token) {
_position.innerFeeGrowth0Token = innerFeeGrowth0Token;
fees0 = uint128(FullMath.mulDiv(innerFeeGrowth0Token - _innerFeeGrowth0Token, currentLiquidity, Constants.Q128));
}
uint128 fees1;
if (innerFeeGrowth1Token != _innerFeeGrowth1Token) {
_position.innerFeeGrowth1Token = innerFeeGrowth1Token;
fees1 = uint128(FullMath.mulDiv(innerFeeGrowth1Token - _innerFeeGrowth1Token, currentLiquidity, Constants.Q128));
}
// To avoid overflow owner has to collect fee before it
if (fees0 | fees1 != 0) {
_position.fees0 += fees0;
_position.fees1 += fees1;
}
}
struct UpdatePositionCache {
uint160 price; // The square root of the current price in Q64.96 format
int24 tick; // The current tick
uint16 timepointIndex; // The index of the last written timepoint
}
/**
* @dev Updates position's ticks and its fees
* @return position The Position object to operate with
* @return amount0 The amount of token0 the caller needs to send, negative if the pool needs to send it
* @return amount1 The amount of token1 the caller needs to send, negative if the pool needs to send it
*/
function _updatePositionTicksAndFees(
address owner,
int24 bottomTick,
int24 topTick,
int128 liquidityDelta
)
private
returns (
Position storage position,
int256 amount0,
int256 amount1
)
{
UpdatePositionCache memory cache = UpdatePositionCache(globalState.price, globalState.tick, globalState.timepointIndex);
position = getOrCreatePosition(owner, bottomTick, topTick);
(uint256 _totalFeeGrowth0Token, uint256 _totalFeeGrowth1Token) = (totalFeeGrowth0Token, totalFeeGrowth1Token);
bool toggledBottom;
bool toggledTop;
if (liquidityDelta != 0) {
uint32 time = _blockTimestamp();
(int56 tickCumulative, uint160 secondsPerLiquidityCumulative, , ) = _getSingleTimepoint(time, 0, cache.tick, cache.timepointIndex, liquidity);
if (
ticks.update(
bottomTick,
cache.tick,
liquidityDelta,
_totalFeeGrowth0Token,
_totalFeeGrowth1Token,
secondsPerLiquidityCumulative,
tickCumulative,
time,
false // isTopTick
)
) {
toggledBottom = true;
tickTable.toggleTick(bottomTick);
}
if (
ticks.update(
topTick,
cache.tick,
liquidityDelta,
_totalFeeGrowth0Token,
_totalFeeGrowth1Token,
secondsPerLiquidityCumulative,
tickCumulative,
time,
true // isTopTick
)
) {
toggledTop = true;
tickTable.toggleTick(topTick);
}
}
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getInnerFeeGrowth(
bottomTick,
topTick,
cache.tick,
_totalFeeGrowth0Token,
_totalFeeGrowth1Token
);
_recalculatePosition(position, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
if (liquidityDelta != 0) {
// if liquidityDelta is negative and the tick was toggled, it means that it should not be initialized anymore, so we delete it
if (liquidityDelta < 0) {
if (toggledBottom) delete ticks[bottomTick];
if (toggledTop) delete ticks[topTick];
}
int128 globalLiquidityDelta;
(amount0, amount1, globalLiquidityDelta) = _getAmountsForLiquidity(bottomTick, topTick, liquidityDelta, cache.tick, cache.price);
if (globalLiquidityDelta != 0) {
uint128 liquidityBefore = liquidity;
uint16 newTimepointIndex = _writeTimepoint(cache.timepointIndex, _blockTimestamp(), cache.tick, liquidityBefore, volumePerLiquidityInBlock);
if (cache.timepointIndex != newTimepointIndex) {
globalState.fee = _getNewFee(_blockTimestamp(), cache.tick, newTimepointIndex, liquidityBefore);
globalState.timepointIndex = newTimepointIndex;
volumePerLiquidityInBlock = 0;
}
liquidity = LiquidityMath.addDelta(liquidityBefore, liquidityDelta);
}
}
}
function _getAmountsForLiquidity(
int24 bottomTick,
int24 topTick,
int128 liquidityDelta,
int24 currentTick,
uint160 currentPrice
)
private
pure
returns (
int256 amount0,
int256 amount1,
int128 globalLiquidityDelta
)
{
// If current tick is less than the provided bottom one then only the token0 has to be provided
if (currentTick < bottomTick) {
amount0 = TokenDeltaMath.getToken0Delta(TickMath.getSqrtRatioAtTick(bottomTick), TickMath.getSqrtRatioAtTick(topTick), liquidityDelta);
} else if (currentTick < topTick) {
amount0 = TokenDeltaMath.getToken0Delta(currentPrice, TickMath.getSqrtRatioAtTick(topTick), liquidityDelta);
amount1 = TokenDeltaMath.getToken1Delta(TickMath.getSqrtRatioAtTick(bottomTick), currentPrice, liquidityDelta);
globalLiquidityDelta = liquidityDelta;
}
// If current tick is greater than the provided top one then only the token1 has to be provided
else {
amount1 = TokenDeltaMath.getToken1Delta(TickMath.getSqrtRatioAtTick(bottomTick), TickMath.getSqrtRatioAtTick(topTick), liquidityDelta);
}
}
/**
* @notice This function fetches certain position object
* @param owner The address owing the position
* @param bottomTick The position's bottom tick
* @param topTick The position's top tick
* @return position The Position object
*/
function getOrCreatePosition(
address owner,
int24 bottomTick,
int24 topTick
) private view returns (Position storage) {
bytes32 key;
assembly {
key := or(shl(24, or(shl(24, owner), and(bottomTick, 0xFFFFFF))), and(topTick, 0xFFFFFF))
}
return positions[key];
}
/// @inheritdoc IAlgebraPoolActions
function mint(
address sender,
address recipient,
int24 bottomTick,
int24 topTick,
uint128 liquidityDesired,
bytes calldata data
)
external
override
lock
onlyValidTicks(bottomTick, topTick)
returns (
uint256 amount0,
uint256 amount1,
uint128 liquidityActual
)
{
require(liquidityDesired > 0, 'IL');
{
(int256 amount0Int, int256 amount1Int, ) = _getAmountsForLiquidity(
bottomTick,
topTick,
int256(liquidityDesired).toInt128(),
globalState.tick,
globalState.price
);
amount0 = uint256(amount0Int);
amount1 = uint256(amount1Int);
}
uint256 receivedAmount0;
uint256 receivedAmount1;
{
if (amount0 > 0) receivedAmount0 = balanceToken0();
if (amount1 > 0) receivedAmount1 = balanceToken1();
IAlgebraMintCallback(msg.sender).algebraMintCallback(amount0, amount1, data);
if (amount0 > 0) require((receivedAmount0 = balanceToken0() - receivedAmount0) > 0, 'IIAM');
if (amount1 > 0) require((receivedAmount1 = balanceToken1() - receivedAmount1) > 0, 'IIAM');
}
liquidityActual = liquidityDesired;
if (receivedAmount0 < amount0) {
liquidityActual = uint128(FullMath.mulDiv(uint256(liquidityActual), receivedAmount0, amount0));
}
if (receivedAmount1 < amount1) {
uint128 liquidityForRA1 = uint128(FullMath.mulDiv(uint256(liquidityActual), receivedAmount1, amount1));
if (liquidityForRA1 < liquidityActual) {
liquidityActual = liquidityForRA1;
}
}
require(liquidityActual > 0, 'IIL2');
{
(, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees(recipient, bottomTick, topTick, int256(liquidityActual).toInt128());
require((amount0 = uint256(amount0Int)) <= receivedAmount0, 'IIAM2');
require((amount1 = uint256(amount1Int)) <= receivedAmount1, 'IIAM2');
}
if (receivedAmount0 > amount0) {
TransferHelper.safeTransfer(token0, sender, receivedAmount0 - amount0);
}
if (receivedAmount1 > amount1) {
TransferHelper.safeTransfer(token1, sender, receivedAmount1 - amount1);
}
emit Mint(msg.sender, recipient, bottomTick, topTick, liquidityActual, amount0, amount1);
}
/// @inheritdoc IAlgebraPoolActions
function collect(
address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount0Requested,
uint128 amount1Requested
) external override lock returns (uint128 amount0, uint128 amount1) {
Position storage position = getOrCreatePosition(msg.sender, bottomTick, topTick);
(uint128 positionFees0, uint128 positionFees1) = (position.fees0, position.fees1);
amount0 = amount0Requested > positionFees0 ? positionFees0 : amount0Requested;
amount1 = amount1Requested > positionFees1 ? positionFees1 : amount1Requested;
if (amount0 | amount1 != 0) {
position.fees0 = positionFees0 - amount0;
position.fees1 = positionFees1 - amount1;
if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0);
if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1);
}
emit Collect(msg.sender, recipient, bottomTick, topTick, amount0, amount1);
}
/// @inheritdoc IAlgebraPoolActions
function burn(
int24 bottomTick,
int24 topTick,
uint128 amount
) external override lock onlyValidTicks(bottomTick, topTick) returns (uint256 amount0, uint256 amount1) {
(Position storage position, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees(
msg.sender,
bottomTick,
topTick,
-int256(amount).toInt128()
);
amount0 = uint256(-amount0Int);
amount1 = uint256(-amount1Int);
if (amount0 | amount1 != 0) {
(position.fees0, position.fees1) = (position.fees0.add128(uint128(amount0)), position.fees1.add128(uint128(amount1)));
}
emit Burn(msg.sender, bottomTick, topTick, amount, amount0, amount1);
}
/// @dev Returns new fee according combination of sigmoids
function _getNewFee(
uint32 _time,
int24 _tick,
uint16 _index,
uint128 _liquidity
) private returns (uint16 newFee) {
newFee = IDataStorageOperator(dataStorageOperator).getFee(_time, _tick, _index, _liquidity);
emit Fee(newFee);
}
function _payCommunityFee(address token, uint256 amount) private {
address vault = IAlgebraFactory(factory).vaultAddress();
TransferHelper.safeTransfer(token, vault, amount);
}
function _writeTimepoint(
uint16 timepointIndex,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint128 volumePerLiquidityInBlock
) private returns (uint16 newTimepointIndex) {
return IDataStorageOperator(dataStorageOperator).write(timepointIndex, blockTimestamp, tick, liquidity, volumePerLiquidityInBlock);
}
function _getSingleTimepoint(
uint32 blockTimestamp,
uint32 secondsAgo,
int24 startTick,
uint16 timepointIndex,
uint128 liquidityStart
)
private
view
returns (
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint112 volatilityCumulative,
uint256 volumePerAvgLiquidity
)
{
return IDataStorageOperator(dataStorageOperator).getSingleTimepoint(blockTimestamp, secondsAgo, startTick, timepointIndex, liquidityStart);
}
function _swapCallback(
int256 amount0,
int256 amount1,
bytes calldata data
) private {
IAlgebraSwapCallback(msg.sender).algebraSwapCallback(amount0, amount1, data);
}
/// @inheritdoc IAlgebraPoolActions
function swap(
address recipient,
bool zeroToOne,
int256 amountRequired,
uint160 limitSqrtPrice,
bytes calldata data
) external override returns (int256 amount0, int256 amount1) {
uint160 currentPrice;
int24 currentTick;
uint128 currentLiquidity;
uint256 communityFee;
// function _calculateSwapAndLock locks globalState.unlocked and does not release
(amount0, amount1, currentPrice, currentTick, currentLiquidity, communityFee) = _calculateSwapAndLock(zeroToOne, amountRequired, limitSqrtPrice);
if (zeroToOne) {
if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); // transfer to recipient
uint256 balance0Before = balanceToken0();
_swapCallback(amount0, amount1, data); // callback to get tokens from the caller
require(balance0Before.add(uint256(amount0)) <= balanceToken0(), 'IIA');
} else {
if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); // transfer to recipient
uint256 balance1Before = balanceToken1();
_swapCallback(amount0, amount1, data); // callback to get tokens from the caller
require(balance1Before.add(uint256(amount1)) <= balanceToken1(), 'IIA');
}
if (communityFee > 0) {
_payCommunityFee(zeroToOne ? token0 : token1, communityFee);
}
emit Swap(msg.sender, recipient, amount0, amount1, currentPrice, currentLiquidity, currentTick);
globalState.unlocked = true; // release after lock in _calculateSwapAndLock
}
/// @inheritdoc IAlgebraPoolActions
function swapSupportingFeeOnInputTokens(
address sender,
address recipient,
bool zeroToOne,
int256 amountRequired,
uint160 limitSqrtPrice,
bytes calldata data
) external override returns (int256 amount0, int256 amount1) {
// Since the pool can get less tokens then sent, firstly we are getting tokens from the
// original caller of the transaction. And change the _amountRequired_
require(globalState.unlocked, 'LOK');
globalState.unlocked = false;
if (zeroToOne) {
uint256 balance0Before = balanceToken0();
_swapCallback(amountRequired, 0, data);
require((amountRequired = int256(balanceToken0().sub(balance0Before))) > 0, 'IIA');
} else {
uint256 balance1Before = balanceToken1();
_swapCallback(0, amountRequired, data);
require((amountRequired = int256(balanceToken1().sub(balance1Before))) > 0, 'IIA');
}
globalState.unlocked = true;
uint160 currentPrice;
int24 currentTick;
uint128 currentLiquidity;
uint256 communityFee;
// function _calculateSwapAndLock locks 'globalState.unlocked' and does not release
(amount0, amount1, currentPrice, currentTick, currentLiquidity, communityFee) = _calculateSwapAndLock(zeroToOne, amountRequired, limitSqrtPrice);
// only transfer to the recipient
if (zeroToOne) {
if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1));
// return the leftovers
if (amount0 < amountRequired) TransferHelper.safeTransfer(token0, sender, uint256(amountRequired.sub(amount0)));
} else {
if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0));
// return the leftovers
if (amount1 < amountRequired) TransferHelper.safeTransfer(token1, sender, uint256(amountRequired.sub(amount1)));
}
if (communityFee > 0) {
_payCommunityFee(zeroToOne ? token0 : token1, communityFee);
}
emit Swap(msg.sender, recipient, amount0, amount1, currentPrice, currentLiquidity, currentTick);
globalState.unlocked = true; // release after lock in _calculateSwapAndLock
}
struct SwapCalculationCache {
uint256 communityFee; // The community fee of the selling token, uint256 to minimize casts
uint128 volumePerLiquidityInBlock;
int56 tickCumulative; // The global tickCumulative at the moment
uint160 secondsPerLiquidityCumulative; // The global secondPerLiquidity at the moment
bool computedLatestTimepoint; // if we have already fetched _tickCumulative_ and _secondPerLiquidity_ from the DataOperator
int256 amountRequiredInitial; // The initial value of the exact input\output amount
int256 amountCalculated; // The additive amount of total output\input calculated trough the swap
uint256 totalFeeGrowth; // The initial totalFeeGrowth + the fee growth during a swap
uint256 totalFeeGrowthB;
IAlgebraVirtualPool.Status incentiveStatus; // If there is an active incentive at the moment
bool exactInput; // Whether the exact input or output is specified
uint16 fee; // The current dynamic fee
int24 startTick; // The tick at the start of a swap
uint16 timepointIndex; // The index of last written timepoint
}
struct PriceMovementCache {
uint160 stepSqrtPrice; // The Q64.96 sqrt of the price at the start of the step
int24 nextTick; // The tick till the current step goes
bool initialized; // True if the _nextTick is initialized
uint160 nextTickPrice; // The Q64.96 sqrt of the price calculated from the _nextTick
uint256 input; // The additive amount of tokens that have been provided
uint256 output; // The additive amount of token that have been withdrawn
uint256 feeAmount; // The total amount of fee earned within a current step
}
/// @notice For gas optimization, locks 'globalState.unlocked' and does not release.
function _calculateSwapAndLock(
bool zeroToOne,
int256 amountRequired,
uint160 limitSqrtPrice
)
private
returns (
int256 amount0,
int256 amount1,
uint160 currentPrice,
int24 currentTick,
uint128 currentLiquidity,
uint256 communityFeeAmount
)
{
uint32 blockTimestamp;
SwapCalculationCache memory cache;
{
// load from one storage slot
currentPrice = globalState.price;
currentTick = globalState.tick;
cache.fee = globalState.fee;
cache.timepointIndex = globalState.timepointIndex;
uint256 _communityFeeToken0 = globalState.communityFeeToken0;
uint256 _communityFeeToken1 = globalState.communityFeeToken1;
bool unlocked = globalState.unlocked;
globalState.unlocked = false; // lock will not be released in this function
require(unlocked, 'LOK');
require(amountRequired != 0, 'AS');
(cache.amountRequiredInitial, cache.exactInput) = (amountRequired, amountRequired > 0);
(currentLiquidity, cache.volumePerLiquidityInBlock) = (liquidity, volumePerLiquidityInBlock);
if (zeroToOne) {
require(limitSqrtPrice < currentPrice && limitSqrtPrice > TickMath.MIN_SQRT_RATIO, 'SPL');
cache.totalFeeGrowth = totalFeeGrowth0Token;
cache.communityFee = _communityFeeToken0;
} else {
require(limitSqrtPrice > currentPrice && limitSqrtPrice < TickMath.MAX_SQRT_RATIO, 'SPL');
cache.totalFeeGrowth = totalFeeGrowth1Token;
cache.communityFee = _communityFeeToken1;
}
cache.startTick = currentTick;
blockTimestamp = _blockTimestamp();
if (activeIncentive != address(0)) {
IAlgebraVirtualPool.Status _status = IAlgebraVirtualPool(activeIncentive).increaseCumulative(blockTimestamp);
if (_status == IAlgebraVirtualPool.Status.NOT_EXIST) {
activeIncentive = address(0);
} else if (_status == IAlgebraVirtualPool.Status.ACTIVE) {
cache.incentiveStatus = IAlgebraVirtualPool.Status.ACTIVE;
} else if (_status == IAlgebraVirtualPool.Status.NOT_STARTED) {
cache.incentiveStatus = IAlgebraVirtualPool.Status.NOT_STARTED;
}
}
uint16 newTimepointIndex = _writeTimepoint(
cache.timepointIndex,
blockTimestamp,
cache.startTick,
currentLiquidity,
cache.volumePerLiquidityInBlock
);
// new timepoint appears only for first swap in block
if (newTimepointIndex != cache.timepointIndex) {
cache.timepointIndex = newTimepointIndex;
cache.volumePerLiquidityInBlock = 0;
cache.fee = _getNewFee(blockTimestamp, currentTick, newTimepointIndex, currentLiquidity);
}
}
PriceMovementCache memory step;
// swap until there is remaining input or output tokens or we reach the price limit
while (true) {
step.stepSqrtPrice = currentPrice;
(step.nextTick, step.initialized) = tickTable.nextTickInTheSameRow(currentTick, zeroToOne);
step.nextTickPrice = TickMath.getSqrtRatioAtTick(step.nextTick);
// calculate the amounts needed to move the price to the next target if it is possible or as much as possible
(currentPrice, step.input, step.output, step.feeAmount) = PriceMovementMath.movePriceTowardsTarget(
zeroToOne,
currentPrice,
(zeroToOne == (step.nextTickPrice < limitSqrtPrice)) // move the price to the target or to the limit
? limitSqrtPrice
: step.nextTickPrice,
currentLiquidity,
amountRequired,
cache.fee
);
if (cache.exactInput) {
amountRequired -= (step.input + step.feeAmount).toInt256(); // decrease remaining input amount
cache.amountCalculated = cache.amountCalculated.sub(step.output.toInt256()); // decrease calculated output amount
} else {
amountRequired += step.output.toInt256(); // increase remaining output amount (since its negative)
cache.amountCalculated = cache.amountCalculated.add((step.input + step.feeAmount).toInt256()); // increase calculated input amount
}
if (cache.communityFee > 0) {
uint256 delta = (step.feeAmount.mul(cache.communityFee)) / Constants.COMMUNITY_FEE_DENOMINATOR;
step.feeAmount -= delta;
communityFeeAmount += delta;
}
if (currentLiquidity > 0) cache.totalFeeGrowth += FullMath.mulDiv(step.feeAmount, Constants.Q128, currentLiquidity);
if (currentPrice == step.nextTickPrice) {
// if the reached tick is initialized then we need to cross it
if (step.initialized) {
// once at a swap we have to get the last timepoint of the observation
if (!cache.computedLatestTimepoint) {
(cache.tickCumulative, cache.secondsPerLiquidityCumulative, , ) = _getSingleTimepoint(
blockTimestamp,
0,
cache.startTick,
cache.timepointIndex,
currentLiquidity // currentLiquidity can be changed only after computedLatestTimepoint
);
cache.computedLatestTimepoint = true;
cache.totalFeeGrowthB = zeroToOne ? totalFeeGrowth1Token : totalFeeGrowth0Token;
}
// every tick cross is needed to be duplicated in a virtual pool
if (cache.incentiveStatus != IAlgebraVirtualPool.Status.NOT_EXIST) {
IAlgebraVirtualPool(activeIncentive).cross(step.nextTick, zeroToOne);
}
int128 liquidityDelta;
if (zeroToOne) {
liquidityDelta = -ticks.cross(
step.nextTick,
cache.totalFeeGrowth, // A == 0
cache.totalFeeGrowthB, // B == 1
cache.secondsPerLiquidityCumulative,
cache.tickCumulative,
blockTimestamp
);
} else {
liquidityDelta = ticks.cross(
step.nextTick,
cache.totalFeeGrowthB, // B == 0
cache.totalFeeGrowth, // A == 1
cache.secondsPerLiquidityCumulative,
cache.tickCumulative,
blockTimestamp
);
}
currentLiquidity = LiquidityMath.addDelta(currentLiquidity, liquidityDelta);
}
currentTick = zeroToOne ? step.nextTick - 1 : step.nextTick;
} else if (currentPrice != step.stepSqrtPrice) {
// if the price has changed but hasn't reached the target
currentTick = TickMath.getTickAtSqrtRatio(currentPrice);
break; // since the price hasn't reached the target, amountRequired should be 0
}
// check stop condition
if (amountRequired == 0 || currentPrice == limitSqrtPrice) {
break;
}
}
(amount0, amount1) = zeroToOne == cache.exactInput // the amount to provide could be less then initially specified (e.g. reached limit)
? (cache.amountRequiredInitial - amountRequired, cache.amountCalculated) // the amount to get could be less then initially specified (e.g. reached limit)
: (cache.amountCalculated, cache.amountRequiredInitial - amountRequired);
(globalState.price, globalState.tick, globalState.fee, globalState.timepointIndex) = (currentPrice, currentTick, cache.fee, cache.timepointIndex);
(liquidity, volumePerLiquidityInBlock) = (
currentLiquidity,
cache.volumePerLiquidityInBlock + IDataStorageOperator(dataStorageOperator).calculateVolumePerLiquidity(currentLiquidity, amount0, amount1)
);
if (zeroToOne) {
totalFeeGrowth0Token = cache.totalFeeGrowth;
} else {
totalFeeGrowth1Token = cache.totalFeeGrowth;
}
}
/// @inheritdoc IAlgebraPoolActions
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external override lock {
uint128 _liquidity = liquidity;
require(_liquidity > 0, 'L');
uint16 _fee = globalState.fee;
uint256 fee0;
uint256 balance0Before = balanceToken0();
if (amount0 > 0) {
fee0 = FullMath.mulDivRoundingUp(amount0, _fee, 1e6);
TransferHelper.safeTransfer(token0, recipient, amount0);
}
uint256 fee1;
uint256 balance1Before = balanceToken1();
if (amount1 > 0) {
fee1 = FullMath.mulDivRoundingUp(amount1, _fee, 1e6);
TransferHelper.safeTransfer(token1, recipient, amount1);
}
IAlgebraFlashCallback(msg.sender).algebraFlashCallback(fee0, fee1, data);
address vault = IAlgebraFactory(factory).vaultAddress();
uint256 paid0 = balanceToken0();
require(balance0Before.add(fee0) <= paid0, 'F0');
paid0 -= balance0Before;
if (paid0 > 0) {
uint8 _communityFeeToken0 = globalState.communityFeeToken0;
uint256 fees0;
if (_communityFeeToken0 > 0) {
fees0 = (paid0 * _communityFeeToken0) / Constants.COMMUNITY_FEE_DENOMINATOR;
TransferHelper.safeTransfer(token0, vault, fees0);
}
totalFeeGrowth0Token += FullMath.mulDiv(paid0 - fees0, Constants.Q128, _liquidity);
}
uint256 paid1 = balanceToken1();
require(balance1Before.add(fee1) <= paid1, 'F1');
paid1 -= balance1Before;
if (paid1 > 0) {
uint8 _communityFeeToken1 = globalState.communityFeeToken1;
uint256 fees1;
if (_communityFeeToken1 > 0) {
fees1 = (paid1 * _communityFeeToken1) / Constants.COMMUNITY_FEE_DENOMINATOR;
TransferHelper.safeTransfer(token1, vault, fees1);
}
totalFeeGrowth1Token += FullMath.mulDiv(paid1 - fees1, Constants.Q128, _liquidity);
}
emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1);
}
/// @inheritdoc IAlgebraPoolPermissionedActions
function setCommunityFee(uint8 communityFee0, uint8 communityFee1) external override lock onlyFactoryOwner {
require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE));
(globalState.communityFeeToken0, globalState.communityFeeToken1) = (communityFee0, communityFee1);
emit CommunityFee(communityFee0, communityFee1);
}
/// @inheritdoc IAlgebraPoolPermissionedActions
function setIncentive(address virtualPoolAddress) external override {
require(msg.sender == IAlgebraFactory(factory).farmingAddress());
activeIncentive = virtualPoolAddress;
emit Incentive(virtualPoolAddress);
}
/// @inheritdoc IAlgebraPoolPermissionedActions
function setLiquidityCooldown(uint32 newLiquidityCooldown) external override onlyFactoryOwner {
require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown);
liquidityCooldown = newLiquidityCooldown;
emit LiquidityCooldown(newLiquidityCooldown);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
pragma abicoder v2;
import './interfaces/IAlgebraFactory.sol';
import './interfaces/IAlgebraPoolDeployer.sol';
import './interfaces/IDataStorageOperator.sol';
import './libraries/AdaptiveFee.sol';
import './DataStorageOperator.sol';
/**
* @title Algebra factory
* @notice Is used to deploy pools and its dataStorages
*/
contract AlgebraFactory is IAlgebraFactory {
/// @inheritdoc IAlgebraFactory
address public override owner;
/// @inheritdoc IAlgebraFactory
address public immutable override poolDeployer;
/// @inheritdoc IAlgebraFactory
address public override farmingAddress;
/// @inheritdoc IAlgebraFactory
address public override vaultAddress;
// values of constants for sigmoids in fee calculation formula
AdaptiveFee.Configuration public baseFeeConfiguration =
AdaptiveFee.Configuration(
3000 - Constants.BASE_FEE, // alpha1
15000 - 3000, // alpha2
360, // beta1
60000, // beta2
59, // gamma1
8500, // gamma2
0, // volumeBeta
10, // volumeGamma
Constants.BASE_FEE // baseFee
);
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/// @inheritdoc IAlgebraFactory
mapping(address => mapping(address => address)) public override poolByPair;
constructor(address _poolDeployer, address _vaultAddress) {
owner = msg.sender;
emit Owner(msg.sender);
poolDeployer = _poolDeployer;
vaultAddress = _vaultAddress;
}
/// @inheritdoc IAlgebraFactory
function createPool(address tokenA, address tokenB) external override returns (address pool) {
require(tokenA != tokenB);
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0));
require(poolByPair[token0][token1] == address(0));
IDataStorageOperator dataStorage = new DataStorageOperator(computeAddress(token0, token1));
dataStorage.changeFeeConfiguration(baseFeeConfiguration);
pool = IAlgebraPoolDeployer(poolDeployer).deploy(address(dataStorage), address(this), token0, token1);
poolByPair[token0][token1] = pool; // to avoid future addresses comparing we are populating the mapping twice
poolByPair[token1][token0] = pool;
emit Pool(token0, token1, pool);
}
/// @inheritdoc IAlgebraFactory
function setOwner(address _owner) external override onlyOwner {
require(owner != _owner);
emit Owner(_owner);
owner = _owner;
}
/// @inheritdoc IAlgebraFactory
function setFarmingAddress(address _farmingAddress) external override onlyOwner {
require(farmingAddress != _farmingAddress);
emit FarmingAddress(_farmingAddress);
farmingAddress = _farmingAddress;
}
/// @inheritdoc IAlgebraFactory
function setVaultAddress(address _vaultAddress) external override onlyOwner {
require(vaultAddress != _vaultAddress);
emit VaultAddress(_vaultAddress);
vaultAddress = _vaultAddress;
}
/// @inheritdoc IAlgebraFactory
function setBaseFeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
) external override onlyOwner {
require(uint256(alpha1) + uint256(alpha2) + uint256(baseFee) <= type(uint16).max, 'Max fee exceeded');
require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0');
baseFeeConfiguration = AdaptiveFee.Configuration(alpha1, alpha2, beta1, beta2, gamma1, gamma2, volumeBeta, volumeGamma, baseFee);
emit FeeConfiguration(alpha1, alpha2, beta1, beta2, gamma1, gamma2, volumeBeta, volumeGamma, baseFee);
}
bytes32 internal constant POOL_INIT_CODE_HASH = 0x6ec6c9c8091d160c0aa74b2b14ba9c1717e95093bd3ac085cee99a49aab294a4;
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param token0 first token
/// @param token1 second token
/// @return pool The contract address of the Algebra pool
function computeAddress(address token0, address token1) internal view returns (address pool) {
pool = address(uint256(keccak256(abi.encodePacked(hex'ff', poolDeployer, keccak256(abi.encode(token0, token1)), POOL_INIT_CODE_HASH))));
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title The interface for the Algebra Factory
* @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraFactory {
/**
* @notice Emitted when the owner of the factory is changed
* @param newOwner The owner after the owner was changed
*/
event Owner(address indexed newOwner);
/**
* @notice Emitted when the vault address is changed
* @param newVaultAddress The vault address after the address was changed
*/
event VaultAddress(address indexed newVaultAddress);
/**
* @notice Emitted when a pool is created
* @param token0 The first token of the pool by address sort order
* @param token1 The second token of the pool by address sort order
* @param pool The address of the created pool
*/
event Pool(address indexed token0, address indexed token1, address pool);
/**
* @notice Emitted when the farming address is changed
* @param newFarmingAddress The farming address after the address was changed
*/
event FarmingAddress(address indexed newFarmingAddress);
event FeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
);
/**
* @notice Returns the current owner of the factory
* @dev Can be changed by the current owner via setOwner
* @return The address of the factory owner
*/
function owner() external view returns (address);
/**
* @notice Returns the current poolDeployerAddress
* @return The address of the poolDeployer
*/
function poolDeployer() external view returns (address);
/**
* @dev Is retrieved from the pools to restrict calling
* certain functions not by a tokenomics contract
* @return The tokenomics contract address
*/
function farmingAddress() external view returns (address);
function vaultAddress() external view returns (address);
/**
* @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
* @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
* @param tokenA The contract address of either token0 or token1
* @param tokenB The contract address of the other token
* @return pool The pool address
*/
function poolByPair(address tokenA, address tokenB) external view returns (address pool);
/**
* @notice Creates a pool for the given two tokens and fee
* @param tokenA One of the two tokens in the desired pool
* @param tokenB The other of the two tokens in the desired pool
* @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
* from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
* are invalid.
* @return pool The address of the newly created pool
*/
function createPool(address tokenA, address tokenB) external returns (address pool);
/**
* @notice Updates the owner of the factory
* @dev Must be called by the current owner
* @param _owner The new owner of the factory
*/
function setOwner(address _owner) external;
/**
* @dev updates tokenomics address on the factory
* @param _farmingAddress The new tokenomics contract address
*/
function setFarmingAddress(address _farmingAddress) external;
/**
* @dev updates vault address on the factory
* @param _vaultAddress The new vault contract address
*/
function setVaultAddress(address _vaultAddress) external;
/**
* @notice Changes initial fee configuration for new pools
* @dev changes coefficients for sigmoids: α / (1 + e^( (β-x) / γ))
* alpha1 + alpha2 + baseFee (max possible fee) must be <= type(uint16).max
* gammas must be > 0
* @param alpha1 max value of the first sigmoid
* @param alpha2 max value of the second sigmoid
* @param beta1 shift along the x-axis for the first sigmoid
* @param beta2 shift along the x-axis for the second sigmoid
* @param gamma1 horizontal stretch factor for the first sigmoid
* @param gamma2 horizontal stretch factor for the second sigmoid
* @param volumeBeta shift along the x-axis for the outer volume-sigmoid
* @param volumeGamma horizontal stretch factor the outer volume-sigmoid
* @param baseFee minimum possible fee
*/
function setBaseFeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title An interface for a contract that is capable of deploying Algebra Pools
* @notice A contract that constructs a pool must implement this to pass arguments to the pool
* @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash
* of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain.
* Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraPoolDeployer {
/**
* @notice Emitted when the factory address is changed
* @param factory The factory address after the address was changed
*/
event Factory(address indexed factory);
/**
* @notice Get the parameters to be used in constructing the pool, set transiently during pool creation.
* @dev Called by the pool constructor to fetch the parameters of the pool
* Returns dataStorage The pools associated dataStorage
* Returns factory The factory address
* Returns token0 The first token of the pool by address sort order
* Returns token1 The second token of the pool by address sort order
*/
function parameters()
external
view
returns (
address dataStorage,
address factory,
address token0,
address token1
);
/**
* @dev Deploys a pool with the given parameters by transiently setting the parameters storage slot and then
* clearing it after deploying the pool.
* @param dataStorage The pools associated dataStorage
* @param factory The contract address of the Algebra factory
* @param token0 The first token of the pool by address sort order
* @param token1 The second token of the pool by address sort order
* @return pool The deployed pool's address
*/
function deploy(
address dataStorage,
address factory,
address token0,
address token1
) external returns (address pool);
/**
* @dev Sets the factory address to the poolDeployer for permissioned actions
* @param factory The address of the Algebra factory
*/
function setFactory(address factory) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;
import '../libraries/AdaptiveFee.sol';
interface IDataStorageOperator {
event FeeConfiguration(AdaptiveFee.Configuration feeConfig);
/**
* @notice Returns data belonging to a certain timepoint
* @param index The index of timepoint in the array
* @dev There is more convenient function to fetch a timepoint: observe(). Which requires not an index but seconds
* @return initialized Whether the timepoint has been initialized and the values are safe to use,
* blockTimestamp The timestamp of the observation,
* tickCumulative The tick multiplied by seconds elapsed for the life of the pool as of the timepoint timestamp,
* secondsPerLiquidityCumulative The seconds per in range liquidity for the life of the pool as of the timepoint timestamp,
* volatilityCumulative Cumulative standard deviation for the life of the pool as of the timepoint timestamp,
* averageTick Time-weighted average tick,
* volumePerLiquidityCumulative Cumulative swap volume per liquidity for the life of the pool as of the timepoint timestamp
*/
function timepoints(uint256 index)
external
view
returns (
bool initialized,
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint88 volatilityCumulative,
int24 averageTick,
uint144 volumePerLiquidityCumulative
);
/// @notice Initialize the dataStorage array by writing the first slot. Called once for the lifecycle of the timepoints array
/// @param time The time of the dataStorage initialization, via block.timestamp truncated to uint32
/// @param tick Initial tick
function initialize(uint32 time, int24 tick) external;
/// @dev Reverts if an timepoint at or before the desired timepoint timestamp does not exist.
/// 0 may be passed as `secondsAgo' to return the current cumulative values.
/// If called with a timestamp falling between two timepoints, returns the counterfactual accumulator values
/// at exactly the timestamp between the two timepoints.
/// @param time The current block timestamp
/// @param secondsAgo The amount of time to look back, in seconds, at which point to return an timepoint
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return tickCumulative The cumulative tick since the pool was first initialized, as of `secondsAgo`
/// @return secondsPerLiquidityCumulative The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of `secondsAgo`
/// @return volatilityCumulative The cumulative volatility value since the pool was first initialized, as of `secondsAgo`
/// @return volumePerAvgLiquidity The cumulative volume per liquidity value since the pool was first initialized, as of `secondsAgo`
function getSingleTimepoint(
uint32 time,
uint32 secondsAgo,
int24 tick,
uint16 index,
uint128 liquidity
)
external
view
returns (
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint112 volatilityCumulative,
uint256 volumePerAvgLiquidity
);
/// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
/// @dev Reverts if `secondsAgos` > oldest timepoint
/// @param time The current block.timestamp
/// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an timepoint
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return tickCumulatives The cumulative tick since the pool was first initialized, as of each `secondsAgo`
/// @return secondsPerLiquidityCumulatives The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo`
/// @return volatilityCumulatives The cumulative volatility values since the pool was first initialized, as of each `secondsAgo`
/// @return volumePerAvgLiquiditys The cumulative volume per liquidity values since the pool was first initialized, as of each `secondsAgo`
function getTimepoints(
uint32 time,
uint32[] memory secondsAgos,
int24 tick,
uint16 index,
uint128 liquidity
)
external
view
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
);
/// @notice Returns average volatility in the range from time-WINDOW to time
/// @param time The current block.timestamp
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return TWVolatilityAverage The average volatility in the recent range
/// @return TWVolumePerLiqAverage The average volume per liquidity in the recent range
function getAverages(
uint32 time,
int24 tick,
uint16 index,
uint128 liquidity
) external view returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage);
/// @notice Writes an dataStorage timepoint to the array
/// @dev Writable at most once per block. Index represents the most recently written element. index must be tracked externally.
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param blockTimestamp The timestamp of the new timepoint
/// @param tick The active tick at the time of the new timepoint
/// @param liquidity The total in-range liquidity at the time of the new timepoint
/// @param volumePerLiquidity The gmean(volumes)/liquidity at the time of the new timepoint
/// @return indexUpdated The new index of the most recently written element in the dataStorage array
function write(
uint16 index,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint128 volumePerLiquidity
) external returns (uint16 indexUpdated);
/// @notice Changes fee configuration for the pool
function changeFeeConfiguration(AdaptiveFee.Configuration calldata feeConfig) external;
/// @notice Calculates gmean(volume/liquidity) for block
/// @param liquidity The current in-range pool liquidity
/// @param amount0 Total amount of swapped token0
/// @param amount1 Total amount of swapped token1
/// @return volumePerLiquidity gmean(volume/liquidity) capped by 100000 << 64
function calculateVolumePerLiquidity(
uint128 liquidity,
int256 amount0,
int256 amount1
) external pure returns (uint128 volumePerLiquidity);
/// @return windowLength Length of window used to calculate averages
function window() external view returns (uint32 windowLength);
/// @notice Calculates fee based on combination of sigmoids
/// @param time The current block.timestamp
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return fee The fee in hundredths of a bip, i.e. 1e-6
function getFee(
uint32 time,
int24 tick,
uint16 index,
uint128 liquidity
) external view returns (uint16 fee);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './Constants.sol';
/// @title AdaptiveFee
/// @notice Calculates fee based on combination of sigmoids
library AdaptiveFee {
// alpha1 + alpha2 + baseFee must be <= type(uint16).max
struct Configuration {
uint16 alpha1; // max value of the first sigmoid
uint16 alpha2; // max value of the second sigmoid
uint32 beta1; // shift along the x-axis for the first sigmoid
uint32 beta2; // shift along the x-axis for the second sigmoid
uint16 gamma1; // horizontal stretch factor for the first sigmoid
uint16 gamma2; // horizontal stretch factor for the second sigmoid
uint32 volumeBeta; // shift along the x-axis for the outer volume-sigmoid
uint16 volumeGamma; // horizontal stretch factor the outer volume-sigmoid
uint16 baseFee; // minimum possible fee
}
/// @notice Calculates fee based on formula:
/// baseFee + sigmoidVolume(sigmoid1(volatility, volumePerLiquidity) + sigmoid2(volatility, volumePerLiquidity))
/// maximum value capped by baseFee + alpha1 + alpha2
function getFee(
uint88 volatility,
uint256 volumePerLiquidity,
Configuration memory config
) internal pure returns (uint16 fee) {
uint256 sumOfSigmoids = sigmoid(volatility, config.gamma1, config.alpha1, config.beta1) +
sigmoid(volatility, config.gamma2, config.alpha2, config.beta2);
if (sumOfSigmoids > type(uint16).max) {
// should be impossible, just in case
sumOfSigmoids = type(uint16).max;
}
return uint16(config.baseFee + sigmoid(volumePerLiquidity, config.volumeGamma, uint16(sumOfSigmoids), config.volumeBeta)); // safe since alpha1 + alpha2 + baseFee _must_ be <= type(uint16).max
}
/// @notice calculates α / (1 + e^( (β-x) / γ))
/// that is a sigmoid with a maximum value of α, x-shifted by β, and stretched by γ
/// @dev returns uint256 for fuzzy testing. Guaranteed that the result is not greater than alpha
function sigmoid(
uint256 x,
uint16 g,
uint16 alpha,
uint256 beta
) internal pure returns (uint256 res) {
if (x > beta) {
x = x - beta;
if (x >= 6 * uint256(g)) return alpha; // so x < 19 bits
uint256 g8 = uint256(g)**8; // < 128 bits (8*16)
uint256 ex = exp(x, g, g8); // < 155 bits
res = (alpha * ex) / (g8 + ex); // in worst case: (16 + 155 bits) / 155 bits
// so res <= alpha
} else {
x = beta - x;
if (x >= 6 * uint256(g)) return 0; // so x < 19 bits
uint256 g8 = uint256(g)**8; // < 128 bits (8*16)
uint256 ex = g8 + exp(x, g, g8); // < 156 bits
res = (alpha * g8) / ex; // in worst case: (16 + 128 bits) / 156 bits
// g8 <= ex, so res <= alpha
}
}
/// @notice calculates e^(x/g) * g^8 in a series, since (around zero):
/// e^x = 1 + x + x^2/2 + ... + x^n/n! + ...
/// e^(x/g) = 1 + x/g + x^2/(2*g^2) + ... + x^(n)/(g^n * n!) + ...
function exp(
uint256 x,
uint16 g,
uint256 gHighestDegree
) internal pure returns (uint256 res) {
// calculating:
// g**8 + x * g**7 + (x**2 * g**6) / 2 + (x**3 * g**5) / 6 + (x**4 * g**4) / 24 + (x**5 * g**3) / 120 + (x**6 * g^2) / 720 + x**7 * g / 5040 + x**8 / 40320
// x**8 < 152 bits (19*8) and g**8 < 128 bits (8*16)
// so each summand < 152 bits and res < 155 bits
uint256 xLowestDegree = x;
res = gHighestDegree; // g**8
gHighestDegree /= g; // g**7
res += xLowestDegree * gHighestDegree;
gHighestDegree /= g; // g**6
xLowestDegree *= x; // x**2
res += (xLowestDegree * gHighestDegree) / 2;
gHighestDegree /= g; // g**5
xLowestDegree *= x; // x**3
res += (xLowestDegree * gHighestDegree) / 6;
gHighestDegree /= g; // g**4
xLowestDegree *= x; // x**4
res += (xLowestDegree * gHighestDegree) / 24;
gHighestDegree /= g; // g**3
xLowestDegree *= x; // x**5
res += (xLowestDegree * gHighestDegree) / 120;
gHighestDegree /= g; // g**2
xLowestDegree *= x; // x**6
res += (xLowestDegree * gHighestDegree) / 720;
xLowestDegree *= x; // x**7
res += (xLowestDegree * g) / 5040 + (xLowestDegree * x) / (40320);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
pragma abicoder v2;
import './interfaces/IAlgebraFactory.sol';
import './interfaces/IDataStorageOperator.sol';
import './libraries/DataStorage.sol';
import './libraries/Sqrt.sol';
import './libraries/AdaptiveFee.sol';
import './libraries/Constants.sol';
contract DataStorageOperator is IDataStorageOperator {
uint256 constant UINT16_MODULO = 65536;
uint128 constant MAX_VOLUME_PER_LIQUIDITY = 100000 << 64; // maximum meaningful ratio of volume to liquidity
using DataStorage for DataStorage.Timepoint[UINT16_MODULO];
DataStorage.Timepoint[UINT16_MODULO] public override timepoints;
AdaptiveFee.Configuration public feeConfig;
address private immutable pool;
address private immutable factory;
modifier onlyPool() {
require(msg.sender == pool, 'only pool can call this');
_;
}
constructor(address _pool) {
factory = msg.sender;
pool = _pool;
}
/// @inheritdoc IDataStorageOperator
function initialize(uint32 time, int24 tick) external override onlyPool {
return timepoints.initialize(time, tick);
}
/// @inheritdoc IDataStorageOperator
function changeFeeConfiguration(AdaptiveFee.Configuration calldata _feeConfig) external override {
require(msg.sender == factory || msg.sender == IAlgebraFactory(factory).owner());
require(uint256(_feeConfig.alpha1) + uint256(_feeConfig.alpha2) + uint256(_feeConfig.baseFee) <= type(uint16).max, 'Max fee exceeded');
require(_feeConfig.gamma1 != 0 && _feeConfig.gamma2 != 0 && _feeConfig.volumeGamma != 0, 'Gammas must be > 0');
feeConfig = _feeConfig;
emit FeeConfiguration(_feeConfig);
}
/// @inheritdoc IDataStorageOperator
function getSingleTimepoint(
uint32 time,
uint32 secondsAgo,
int24 tick,
uint16 index,
uint128 liquidity
)
external
view
override
onlyPool
returns (
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint112 volatilityCumulative,
uint256 volumePerAvgLiquidity
)
{
uint16 oldestIndex;
// check if we have overflow in the past
uint16 nextIndex = index + 1; // considering overflow
if (timepoints[nextIndex].initialized) {
oldestIndex = nextIndex;
}
DataStorage.Timepoint memory result = timepoints.getSingleTimepoint(time, secondsAgo, tick, index, oldestIndex, liquidity);
(tickCumulative, secondsPerLiquidityCumulative, volatilityCumulative, volumePerAvgLiquidity) = (
result.tickCumulative,
result.secondsPerLiquidityCumulative,
result.volatilityCumulative,
result.volumePerLiquidityCumulative
);
}
/// @inheritdoc IDataStorageOperator
function getTimepoints(
uint32 time,
uint32[] memory secondsAgos,
int24 tick,
uint16 index,
uint128 liquidity
)
external
view
override
onlyPool
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
)
{
return timepoints.getTimepoints(time, secondsAgos, tick, index, liquidity);
}
/// @inheritdoc IDataStorageOperator
function getAverages(
uint32 time,
int24 tick,
uint16 index,
uint128 liquidity
) external view override onlyPool returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage) {
return timepoints.getAverages(time, tick, index, liquidity);
}
/// @inheritdoc IDataStorageOperator
function write(
uint16 index,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint128 volumePerLiquidity
) external override onlyPool returns (uint16 indexUpdated) {
return timepoints.write(index, blockTimestamp, tick, liquidity, volumePerLiquidity);
}
/// @inheritdoc IDataStorageOperator
function calculateVolumePerLiquidity(
uint128 liquidity,
int256 amount0,
int256 amount1
) external pure override returns (uint128 volumePerLiquidity) {
uint256 volume = Sqrt.sqrtAbs(amount0) * Sqrt.sqrtAbs(amount1);
uint256 volumeShifted;
if (volume >= 2**192) volumeShifted = (type(uint256).max) / (liquidity > 0 ? liquidity : 1);
else volumeShifted = (volume << 64) / (liquidity > 0 ? liquidity : 1);
if (volumeShifted >= MAX_VOLUME_PER_LIQUIDITY) return MAX_VOLUME_PER_LIQUIDITY;
else return uint128(volumeShifted);
}
/// @inheritdoc IDataStorageOperator
function window() external pure override returns (uint32) {
return DataStorage.WINDOW;
}
/// @inheritdoc IDataStorageOperator
function getFee(
uint32 _time,
int24 _tick,
uint16 _index,
uint128 _liquidity
) external view override onlyPool returns (uint16 fee) {
(uint88 volatilityAverage, uint256 volumePerLiqAverage) = timepoints.getAverages(_time, _tick, _index, _liquidity);
return AdaptiveFee.getFee(volatilityAverage / 15, volumePerLiqAverage, feeConfig);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
library Constants {
uint8 internal constant RESOLUTION = 96;
uint256 internal constant Q96 = 0x1000000000000000000000000;
uint256 internal constant Q128 = 0x100000000000000000000000000000000;
// fee value in hundredths of a bip, i.e. 1e-6
uint16 internal constant BASE_FEE = 100;
int24 internal constant TICK_SPACING = 60;
// max(uint128) / ( (MAX_TICK - MIN_TICK) / TICK_SPACING )
uint128 internal constant MAX_LIQUIDITY_PER_TICK = 11505743598341114571880798222544994;
uint32 internal constant MAX_LIQUIDITY_COOLDOWN = 1 days;
uint8 internal constant MAX_COMMUNITY_FEE = 250;
uint256 internal constant COMMUNITY_FEE_DENOMINATOR = 1000;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './FullMath.sol';
/// @title DataStorage
/// @notice Provides price, liquidity, volatility data useful for a wide variety of system designs
/// @dev Instances of stored dataStorage data, "timepoints", are collected in the dataStorage array
/// Timepoints are overwritten when the full length of the dataStorage array is populated.
/// The most recent timepoint is available by passing 0 to getSingleTimepoint()
library DataStorage {
uint32 public constant WINDOW = 1 days;
uint256 private constant UINT16_MODULO = 65536;
struct Timepoint {
bool initialized; // whether or not the timepoint is initialized
uint32 blockTimestamp; // the block timestamp of the timepoint
int56 tickCumulative; // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized
uint160 secondsPerLiquidityCumulative; // the seconds per liquidity since the pool was first initialized
uint88 volatilityCumulative; // the volatility accumulator; overflow after ~34800 years is desired :)
int24 averageTick; // average tick at this blockTimestamp
uint144 volumePerLiquidityCumulative; // the gmean(volumes)/liquidity accumulator
}
/// @notice Calculates volatility between two sequential timepoints with resampling to 1 sec frequency
/// @param dt Timedelta between timepoints, must be within uint32 range
/// @param tick0 The tick at the left timepoint, must be within int24 range
/// @param tick1 The tick at the right timepoint, must be within int24 range
/// @param avgTick0 The average tick at the left timepoint, must be within int24 range
/// @param avgTick1 The average tick at the right timepoint, must be within int24 range
/// @return volatility The volatility between two sequential timepoints
/// If the requirements for the parameters are met, it always fits 88 bits
function _volatilityOnRange(
int256 dt,
int256 tick0,
int256 tick1,
int256 avgTick0,
int256 avgTick1
) internal pure returns (uint256 volatility) {
// On the time interval from the previous timepoint to the current
// we can represent tick and average tick change as two straight lines:
// tick = k*t + b, where k and b are some constants
// avgTick = p*t + q, where p and q are some constants
// we want to get sum of (tick(t) - avgTick(t))^2 for every t in the interval (0; dt]
// so: (tick(t) - avgTick(t))^2 = ((k*t + b) - (p*t + q))^2 = (k-p)^2 * t^2 + 2(k-p)(b-q)t + (b-q)^2
// since everything except t is a constant, we need to use progressions for t and t^2:
// sum(t) for t from 1 to dt = dt*(dt + 1)/2 = sumOfSequence
// sum(t^2) for t from 1 to dt = dt*(dt+1)*(2dt + 1)/6 = sumOfSquares
// so result will be: (k-p)^2 * sumOfSquares + 2(k-p)(b-q)*sumOfSequence + dt*(b-q)^2
int256 K = (tick1 - tick0) - (avgTick1 - avgTick0); // (k - p)*dt
int256 B = (tick0 - avgTick0) * dt; // (b - q)*dt
int256 sumOfSquares = (dt * (dt + 1) * (2 * dt + 1)); // sumOfSquares * 6
int256 sumOfSequence = (dt * (dt + 1)); // sumOfSequence * 2
volatility = uint256((K**2 * sumOfSquares + 6 * B * K * sumOfSequence + 6 * dt * B**2) / (6 * dt**2));
}
/// @notice Transforms a previous timepoint into a new timepoint, given the passage of time and the current tick and liquidity values
/// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows
/// @param last The specified timepoint to be used in creation of new timepoint
/// @param blockTimestamp The timestamp of the new timepoint
/// @param tick The active tick at the time of the new timepoint
/// @param prevTick The active tick at the time of the last timepoint
/// @param liquidity The total in-range liquidity at the time of the new timepoint
/// @param averageTick The average tick at the time of the new timepoint
/// @param volumePerLiquidity The gmean(volumes)/liquidity at the time of the new timepoint
/// @return Timepoint The newly populated timepoint
function createNewTimepoint(
Timepoint memory last,
uint32 blockTimestamp,
int24 tick,
int24 prevTick,
uint128 liquidity,
int24 averageTick,
uint128 volumePerLiquidity
) private pure returns (Timepoint memory) {
uint32 delta = blockTimestamp - last.blockTimestamp;
last.initialized = true;
last.blockTimestamp = blockTimestamp;
last.tickCumulative += int56(tick) * delta;
last.secondsPerLiquidityCumulative += ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)); // just timedelta if liquidity == 0
last.volatilityCumulative += uint88(_volatilityOnRange(delta, prevTick, tick, last.averageTick, averageTick)); // always fits 88 bits
last.averageTick = averageTick;
last.volumePerLiquidityCumulative += volumePerLiquidity;
return last;
}
/// @notice comparator for 32-bit timestamps
/// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to currentTime
/// @param a A comparison timestamp from which to determine the relative position of `currentTime`
/// @param b From which to determine the relative position of `currentTime`
/// @param currentTime A timestamp truncated to 32 bits
/// @return res Whether `a` is chronologically <= `b`
function lteConsideringOverflow(
uint32 a,
uint32 b,
uint32 currentTime
) private pure returns (bool res) {
res = a > currentTime;
if (res == b > currentTime) res = a <= b; // if both are on the same side
}
/// @dev guaranteed that the result is within the bounds of int24
/// returns int256 for fuzzy tests
function _getAverageTick(
Timepoint[UINT16_MODULO] storage self,
uint32 time,
int24 tick,
uint16 index,
uint16 oldestIndex,
uint32 lastTimestamp,
int56 lastTickCumulative
) internal view returns (int256 avgTick) {
uint32 oldestTimestamp = self[oldestIndex].blockTimestamp;
int56 oldestTickCumulative = self[oldestIndex].tickCumulative;
if (lteConsideringOverflow(oldestTimestamp, time - WINDOW, time)) {
if (lteConsideringOverflow(lastTimestamp, time - WINDOW, time)) {
index -= 1; // considering underflow
Timepoint storage startTimepoint = self[index];
avgTick = startTimepoint.initialized
? (lastTickCumulative - startTimepoint.tickCumulative) / (lastTimestamp - startTimepoint.blockTimestamp)
: tick;
} else {
Timepoint memory startOfWindow = getSingleTimepoint(self, time, WINDOW, tick, index, oldestIndex, 0);
// current-WINDOW last current
// _________*____________*_______*_
// ||||||||||||
avgTick = (lastTickCumulative - startOfWindow.tickCumulative) / (lastTimestamp - time + WINDOW);
}
} else {
avgTick = (lastTimestamp == oldestTimestamp) ? tick : (lastTickCumulative - oldestTickCumulative) / (lastTimestamp - oldestTimestamp);
}
}
/// @notice Fetches the timepoints beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied.
/// The result may be the same timepoint, or adjacent timepoints.
/// @dev The answer must be contained in the array, used when the target is located within the stored timepoint
/// boundaries: older than the most recent timepoint and younger, or the same age as, the oldest timepoint
/// @param self The stored dataStorage array
/// @param time The current block.timestamp
/// @param target The timestamp at which the reserved timepoint should be for
/// @param lastIndex The index of the timepoint that was most recently written to the timepoints array
/// @param oldestIndex The index of the oldest timepoint in the timepoints array
/// @return beforeOrAt The timepoint recorded before, or at, the target
/// @return atOrAfter The timepoint recorded at, or after, the target
function binarySearch(
Timepoint[UINT16_MODULO] storage self,
uint32 time,
uint32 target,
uint16 lastIndex,
uint16 oldestIndex
) private view returns (Timepoint storage beforeOrAt, Timepoint storage atOrAfter) {
uint256 left = oldestIndex; // oldest timepoint
uint256 right = lastIndex >= oldestIndex ? lastIndex : lastIndex + UINT16_MODULO; // newest timepoint considering one index overflow
uint256 current = (left + right) >> 1; // "middle" point between the boundaries
do {
beforeOrAt = self[uint16(current)]; // checking the "middle" point between the boundaries
(bool initializedBefore, uint32 timestampBefore) = (beforeOrAt.initialized, beforeOrAt.blockTimestamp);
if (initializedBefore) {
if (lteConsideringOverflow(timestampBefore, target, time)) {
// is current point before or at `target`?
atOrAfter = self[uint16(current + 1)]; // checking the next point after "middle"
(bool initializedAfter, uint32 timestampAfter) = (atOrAfter.initialized, atOrAfter.blockTimestamp);
if (initializedAfter) {
if (lteConsideringOverflow(target, timestampAfter, time)) {
// is the "next" point after or at `target`?
return (beforeOrAt, atOrAfter); // the only fully correct way to finish
}
left = current + 1; // "next" point is before the `target`, so looking in the right half
} else {
// beforeOrAt is initialized and <= target, and next timepoint is uninitialized
// should be impossible if initial boundaries and `target` are correct
return (beforeOrAt, beforeOrAt);
}
} else {
right = current - 1; // current point is after the `target`, so looking in the left half
}
} else {
// we've landed on an uninitialized timepoint, keep searching higher
// should be impossible if initial boundaries and `target` are correct
left = current + 1;
}
current = (left + right) >> 1; // calculating the new "middle" point index after updating the bounds
} while (true);
atOrAfter = beforeOrAt; // code is unreachable, to suppress compiler warning
assert(false);
}
/// @dev Reverts if an timepoint at or before the desired timepoint timestamp does not exist.
/// 0 may be passed as `secondsAgo' to return the current cumulative values.
/// If called with a timestamp falling between two timepoints, returns the counterfactual accumulator values
/// at exactly the timestamp between the two timepoints.
/// @param self The stored dataStorage array
/// @param time The current block timestamp
/// @param secondsAgo The amount of time to look back, in seconds, at which point to return an timepoint
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param oldestIndex The index of the oldest timepoint
/// @param liquidity The current in-range pool liquidity
/// @return targetTimepoint desired timepoint or it's approximation
function getSingleTimepoint(
Timepoint[UINT16_MODULO] storage self,
uint32 time,
uint32 secondsAgo,
int24 tick,
uint16 index,
uint16 oldestIndex,
uint128 liquidity
) internal view returns (Timepoint memory targetTimepoint) {
uint32 target = time - secondsAgo;
// if target is newer than last timepoint
if (secondsAgo == 0 || lteConsideringOverflow(self[index].blockTimestamp, target, time)) {
Timepoint memory last = self[index];
if (last.blockTimestamp == target) {
return last;
} else {
// otherwise, we need to add new timepoint
int24 avgTick = int24(_getAverageTick(self, time, tick, index, oldestIndex, last.blockTimestamp, last.tickCumulative));
int24 prevTick = tick;
{
if (index != oldestIndex) {
Timepoint memory prevLast;
Timepoint storage _prevLast = self[index - 1]; // considering index underflow
prevLast.blockTimestamp = _prevLast.blockTimestamp;
prevLast.tickCumulative = _prevLast.tickCumulative;
prevTick = int24((last.tickCumulative - prevLast.tickCumulative) / (last.blockTimestamp - prevLast.blockTimestamp));
}
}
return createNewTimepoint(last, target, tick, prevTick, liquidity, avgTick, 0);
}
}
require(lteConsideringOverflow(self[oldestIndex].blockTimestamp, target, time), 'OLD');
(Timepoint memory beforeOrAt, Timepoint memory atOrAfter) = binarySearch(self, time, target, index, oldestIndex);
if (target == atOrAfter.blockTimestamp) {
return atOrAfter; // we're at the right boundary
}
if (target != beforeOrAt.blockTimestamp) {
// we're in the middle
uint32 timepointTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp;
uint32 targetDelta = target - beforeOrAt.blockTimestamp;
// For gas savings the resulting point is written to beforeAt
beforeOrAt.tickCumulative += ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / timepointTimeDelta) * targetDelta;
beforeOrAt.secondsPerLiquidityCumulative += uint160(
(uint256(atOrAfter.secondsPerLiquidityCumulative - beforeOrAt.secondsPerLiquidityCumulative) * targetDelta) / timepointTimeDelta
);
beforeOrAt.volatilityCumulative += ((atOrAfter.volatilityCumulative - beforeOrAt.volatilityCumulative) / timepointTimeDelta) * targetDelta;
beforeOrAt.volumePerLiquidityCumulative +=
((atOrAfter.volumePerLiquidityCumulative - beforeOrAt.volumePerLiquidityCumulative) / timepointTimeDelta) *
targetDelta;
}
// we're at the left boundary or at the middle
return beforeOrAt;
}
/// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
/// @dev Reverts if `secondsAgos` > oldest timepoint
/// @param self The stored dataStorage array
/// @param time The current block.timestamp
/// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an timepoint
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo`
/// @return secondsPerLiquidityCumulatives The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo`
/// @return volatilityCumulatives The cumulative volatility values since the pool was first initialized, as of each `secondsAgo`
/// @return volumePerAvgLiquiditys The cumulative volume per liquidity values since the pool was first initialized, as of each `secondsAgo`
function getTimepoints(
Timepoint[UINT16_MODULO] storage self,
uint32 time,
uint32[] memory secondsAgos,
int24 tick,
uint16 index,
uint128 liquidity
)
internal
view
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
)
{
tickCumulatives = new int56[](secondsAgos.length);
secondsPerLiquidityCumulatives = new uint160[](secondsAgos.length);
volatilityCumulatives = new uint112[](secondsAgos.length);
volumePerAvgLiquiditys = new uint256[](secondsAgos.length);
uint16 oldestIndex;
// check if we have overflow in the past
uint16 nextIndex = index + 1; // considering overflow
if (self[nextIndex].initialized) {
oldestIndex = nextIndex;
}
Timepoint memory current;
for (uint256 i = 0; i < secondsAgos.length; i++) {
current = getSingleTimepoint(self, time, secondsAgos[i], tick, index, oldestIndex, liquidity);
(tickCumulatives[i], secondsPerLiquidityCumulatives[i], volatilityCumulatives[i], volumePerAvgLiquiditys[i]) = (
current.tickCumulative,
current.secondsPerLiquidityCumulative,
current.volatilityCumulative,
current.volumePerLiquidityCumulative
);
}
}
/// @notice Returns average volatility in the range from time-WINDOW to time
/// @param self The stored dataStorage array
/// @param time The current block.timestamp
/// @param tick The current tick
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param liquidity The current in-range pool liquidity
/// @return volatilityAverage The average volatility in the recent range
/// @return volumePerLiqAverage The average volume per liquidity in the recent range
function getAverages(
Timepoint[UINT16_MODULO] storage self,
uint32 time,
int24 tick,
uint16 index,
uint128 liquidity
) internal view returns (uint88 volatilityAverage, uint256 volumePerLiqAverage) {
uint16 oldestIndex;
Timepoint storage oldest = self[0];
uint16 nextIndex = index + 1; // considering overflow
if (self[nextIndex].initialized) {
oldest = self[nextIndex];
oldestIndex = nextIndex;
}
Timepoint memory endOfWindow = getSingleTimepoint(self, time, 0, tick, index, oldestIndex, liquidity);
uint32 oldestTimestamp = oldest.blockTimestamp;
if (lteConsideringOverflow(oldestTimestamp, time - WINDOW, time)) {
Timepoint memory startOfWindow = getSingleTimepoint(self, time, WINDOW, tick, index, oldestIndex, liquidity);
return (
(endOfWindow.volatilityCumulative - startOfWindow.volatilityCumulative) / WINDOW,
uint256(endOfWindow.volumePerLiquidityCumulative - startOfWindow.volumePerLiquidityCumulative) >> 57
);
} else if (time != oldestTimestamp) {
uint88 _oldestVolatilityCumulative = oldest.volatilityCumulative;
uint144 _oldestVolumePerLiquidityCumulative = oldest.volumePerLiquidityCumulative;
return (
(endOfWindow.volatilityCumulative - _oldestVolatilityCumulative) / (time - oldestTimestamp),
uint256(endOfWindow.volumePerLiquidityCumulative - _oldestVolumePerLiquidityCumulative) >> 57
);
}
}
/// @notice Initialize the dataStorage array by writing the first slot. Called once for the lifecycle of the timepoints array
/// @param self The stored dataStorage array
/// @param time The time of the dataStorage initialization, via block.timestamp truncated to uint32
/// @param tick Initial tick
function initialize(
Timepoint[UINT16_MODULO] storage self,
uint32 time,
int24 tick
) internal {
require(!self[0].initialized);
self[0].initialized = true;
self[0].blockTimestamp = time;
self[0].averageTick = tick;
}
/// @notice Writes an dataStorage timepoint to the array
/// @dev Writable at most once per block. Index represents the most recently written element. index must be tracked externally.
/// @param self The stored dataStorage array
/// @param index The index of the timepoint that was most recently written to the timepoints array
/// @param blockTimestamp The timestamp of the new timepoint
/// @param tick The active tick at the time of the new timepoint
/// @param liquidity The total in-range liquidity at the time of the new timepoint
/// @param volumePerLiquidity The gmean(volumes)/liquidity at the time of the new timepoint
/// @return indexUpdated The new index of the most recently written element in the dataStorage array
function write(
Timepoint[UINT16_MODULO] storage self,
uint16 index,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint128 volumePerLiquidity
) internal returns (uint16 indexUpdated) {
Timepoint storage _last = self[index];
// early return if we've already written an timepoint this block
if (_last.blockTimestamp == blockTimestamp) {
return index;
}
Timepoint memory last = _last;
// get next index considering overflow
indexUpdated = index + 1;
uint16 oldestIndex;
// check if we have overflow in the past
if (self[indexUpdated].initialized) {
oldestIndex = indexUpdated;
}
int24 avgTick = int24(_getAverageTick(self, blockTimestamp, tick, index, oldestIndex, last.blockTimestamp, last.tickCumulative));
int24 prevTick = tick;
if (index != oldestIndex) {
Timepoint storage _prevLast = self[index - 1]; // considering index underflow
uint32 _prevLastBlockTimestamp = _prevLast.blockTimestamp;
int56 _prevLastTickCumulative = _prevLast.tickCumulative;
prevTick = int24((last.tickCumulative - _prevLastTickCumulative) / (last.blockTimestamp - _prevLastBlockTimestamp));
}
self[indexUpdated] = createNewTimepoint(last, blockTimestamp, tick, prevTick, liquidity, avgTick, volumePerLiquidity);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0;
library Sqrt {
/// @notice Gets the square root of the absolute value of the parameter
function sqrtAbs(int256 _x) internal pure returns (uint256 result) {
// get abs value
int256 mask = _x >> (256 - 1);
uint256 x = uint256((_x ^ mask) - mask);
if (x == 0) result = 0;
else {
uint256 xx = x;
uint256 r = 1;
if (xx >= 0x100000000000000000000000000000000) {
xx >>= 128;
r <<= 64;
}
if (xx >= 0x10000000000000000) {
xx >>= 64;
r <<= 32;
}
if (xx >= 0x100000000) {
xx >>= 32;
r <<= 16;
}
if (xx >= 0x10000) {
xx >>= 16;
r <<= 8;
}
if (xx >= 0x100) {
xx >>= 8;
r <<= 4;
}
if (xx >= 0x10) {
xx >>= 4;
r <<= 2;
}
if (xx >= 0x8) {
r <<= 1;
}
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1; // @dev Seven iterations should be enough.
uint256 r1 = x / r;
result = r < r1 ? r : r1;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0 = a * b; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
assembly {
result := div(prod0, denominator)
}
return result;
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
// Subtract 256 bit remainder from 512 bit number
assembly {
let remainder := mulmod(a, b, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = -denominator & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the preconditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivRoundingUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
if (a == 0 || ((result = a * b) / a == b)) {
require(denominator > 0);
assembly {
result := add(div(result, denominator), gt(mod(result, denominator), 0))
}
} else {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}
/// @notice Returns ceil(x / y)
/// @dev division by 0 has unspecified behavior, and must be checked externally
/// @param x The dividend
/// @param y The divisor
/// @return z The quotient, ceil(x / y)
function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(div(x, y), gt(mod(x, y), 0))
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
pragma abicoder v2;
import '../../interfaces/IAlgebraFactory.sol';
import '../../interfaces/IAlgebraPoolDeployer.sol';
import '../../interfaces/IDataStorageOperator.sol';
import '../../libraries/AdaptiveFee.sol';
import '../../DataStorageOperator.sol';
/**
* @title Algebra factory for simulation
* @notice Is used to deploy pools and its dataStorages
*/
contract SimulationTimeFactory is IAlgebraFactory {
/// @inheritdoc IAlgebraFactory
address public override owner;
/// @inheritdoc IAlgebraFactory
address public immutable override poolDeployer;
/// @inheritdoc IAlgebraFactory
address public override farmingAddress;
/// @inheritdoc IAlgebraFactory
address public override vaultAddress;
// values of constants for sigmoids in fee calculation formula
AdaptiveFee.Configuration public baseFeeConfiguration =
AdaptiveFee.Configuration(
3000 - Constants.BASE_FEE, // alpha1
15000 - 3000, // alpha2
360, // beta1
60000, // beta2
59, // gamma1
8500, // gamma2
0, // volumeBeta
10, // volumeGamma
Constants.BASE_FEE // baseFee
);
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/// @inheritdoc IAlgebraFactory
mapping(address => mapping(address => address)) public override poolByPair;
constructor(address _poolDeployer, address _vaultAddress) {
owner = msg.sender;
emit Owner(msg.sender);
poolDeployer = _poolDeployer;
vaultAddress = _vaultAddress;
}
/// @inheritdoc IAlgebraFactory
function createPool(address tokenA, address tokenB) external override returns (address pool) {
require(tokenA != tokenB);
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0));
require(poolByPair[token0][token1] == address(0));
IDataStorageOperator dataStorage = new DataStorageOperator(computeAddress(token0, token1));
dataStorage.changeFeeConfiguration(baseFeeConfiguration);
pool = IAlgebraPoolDeployer(poolDeployer).deploy(address(dataStorage), address(this), token0, token1);
poolByPair[token0][token1] = pool; // to avoid future addresses comparing we are populating the mapping twice
poolByPair[token1][token0] = pool;
emit Pool(token0, token1, pool);
}
/// @inheritdoc IAlgebraFactory
function setOwner(address _owner) external override onlyOwner {
require(owner != _owner);
emit Owner(_owner);
owner = _owner;
}
/// @inheritdoc IAlgebraFactory
function setFarmingAddress(address _farmingAddress) external override onlyOwner {
require(farmingAddress != _farmingAddress);
emit FarmingAddress(_farmingAddress);
farmingAddress = _farmingAddress;
}
/// @inheritdoc IAlgebraFactory
function setVaultAddress(address _vaultAddress) external override onlyOwner {
require(vaultAddress != _vaultAddress);
emit VaultAddress(_vaultAddress);
vaultAddress = _vaultAddress;
}
/// @inheritdoc IAlgebraFactory
function setBaseFeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
) external override onlyOwner {
require(uint256(alpha1) + uint256(alpha2) + uint256(baseFee) <= type(uint16).max, 'Max fee exceeded');
require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0');
baseFeeConfiguration = AdaptiveFee.Configuration(alpha1, alpha2, beta1, beta2, gamma1, gamma2, volumeBeta, volumeGamma, baseFee);
emit FeeConfiguration(alpha1, alpha2, beta1, beta2, gamma1, gamma2, volumeBeta, volumeGamma, baseFee);
}
bytes32 internal constant POOL_INIT_CODE_HASH = 0x900bf8d45a06958144a51da8749d15e2a339e87243bd50bc88d46815c9ec888d;
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param token0 first token
/// @param token1 second token
/// @return pool The contract address of the Algebra pool
function computeAddress(address token0, address token1) internal view returns (address pool) {
pool = address(uint256(keccak256(abi.encodePacked(hex'ff', poolDeployer, keccak256(abi.encode(token0, token1)), POOL_INIT_CODE_HASH))));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/AdaptiveFee.sol';
import '../libraries/Constants.sol';
contract AdaptiveFeeTest {
AdaptiveFee.Configuration public feeConfig =
AdaptiveFee.Configuration(
3000 - Constants.BASE_FEE, // alpha1
15000 - 3000, // alpha2
360, // beta1
60000, // beta2
59, // gamma1
8500, // gamma2
0, // volumeBeta
10, // volumeGamma
Constants.BASE_FEE // baseFee
);
function getFee(uint88 volatility, uint256 volumePerLiquidity) external view returns (uint256 fee) {
return AdaptiveFee.getFee(volatility, volumePerLiquidity, feeConfig);
}
function getGasCostOfGetFee(uint88 volatility, uint256 volumePerLiquidity) external view returns (uint256) {
uint256 gasBefore = gasleft();
AdaptiveFee.getFee(volatility, volumePerLiquidity, feeConfig);
return gasBefore - gasleft();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/FullMath.sol';
import '../libraries/TokenDeltaMath.sol';
import '../libraries/PriceMovementMath.sol';
import '../libraries/Constants.sol';
contract TokenDeltaMathEchidnaTest {
function mulDivRoundingUpInvariants(
uint256 x,
uint256 y,
uint256 z
) external pure {
require(z > 0);
uint256 notRoundedUp = FullMath.mulDiv(x, y, z);
uint256 roundedUp = FullMath.mulDivRoundingUp(x, y, z);
assert(roundedUp >= notRoundedUp);
assert(roundedUp - notRoundedUp < 2);
if (roundedUp - notRoundedUp == 1) {
assert(mulmod(x, y, z) > 0);
} else {
assert(mulmod(x, y, z) == 0);
}
}
function getNextSqrtPriceFromInputInvariants(
uint160 sqrtP,
uint128 liquidity,
uint256 amountIn,
bool zeroToOne
) external pure {
uint160 sqrtQ = PriceMovementMath.getNewPriceAfterInput(sqrtP, liquidity, amountIn, zeroToOne);
if (zeroToOne) {
assert(sqrtQ <= sqrtP);
assert(amountIn >= TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity, true));
} else {
assert(sqrtQ >= sqrtP);
assert(amountIn >= TokenDeltaMath.getToken1Delta(sqrtP, sqrtQ, liquidity, true));
}
}
function getNextSqrtPriceFromOutputInvariants(
uint160 sqrtP,
uint128 liquidity,
uint256 amountOut,
bool zeroToOne
) external pure {
uint160 sqrtQ = PriceMovementMath.getNewPriceAfterOutput(sqrtP, liquidity, amountOut, zeroToOne);
if (zeroToOne) {
assert(sqrtQ <= sqrtP);
assert(amountOut <= TokenDeltaMath.getToken1Delta(sqrtQ, sqrtP, liquidity, false));
} else {
assert(sqrtQ > 0); // this has to be true, otherwise we need another require
assert(sqrtQ >= sqrtP);
assert(amountOut <= TokenDeltaMath.getToken0Delta(sqrtP, sqrtQ, liquidity, false));
}
}
function getNextSqrtPriceFromAmount0RoundingUpInvariants(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) external pure {
require(sqrtPX96 > 0);
require(liquidity > 0);
uint160 sqrtQX96;
if (add) {
sqrtQX96 = PriceMovementMath.getNewPriceAfterInput(sqrtPX96, liquidity, amount, true);
} else {
sqrtQX96 = PriceMovementMath.getNewPriceAfterOutput(sqrtPX96, liquidity, amount, false);
}
if (add) {
assert(sqrtQX96 <= sqrtPX96);
} else {
assert(sqrtQX96 >= sqrtPX96);
}
if (amount == 0) {
assert(sqrtPX96 == sqrtQX96);
}
}
function getNextSqrtPriceFromAmount1RoundingDownInvariants(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) external pure {
require(sqrtPX96 > 0);
require(liquidity > 0);
uint160 sqrtQX96;
if (add) {
sqrtQX96 = PriceMovementMath.getNewPriceAfterInput(sqrtPX96, liquidity, amount, false);
} else {
sqrtQX96 = PriceMovementMath.getNewPriceAfterOutput(sqrtPX96, liquidity, amount, true);
}
if (add) {
assert(sqrtQX96 >= sqrtPX96);
} else {
assert(sqrtQX96 <= sqrtPX96);
}
if (amount == 0) {
assert(sqrtPX96 == sqrtQX96);
}
}
function getToken0DeltaInvariants(
uint160 sqrtP,
uint160 sqrtQ,
uint128 liquidity
) external pure {
require(sqrtP > 0 && sqrtQ > 0);
if (sqrtP < sqrtQ) (sqrtP, sqrtQ) = (sqrtQ, sqrtP);
uint256 amount0Down = TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity, false);
uint256 amount0Up = TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity, true);
assert(amount0Down <= amount0Up);
// diff is 0 or 1
assert(amount0Up - amount0Down < 2);
}
// ensure that chained division is always equal to the full-precision case for
// liquidity * (sqrt(P) - sqrt(Q)) / (sqrt(P) * sqrt(Q))
function getToken0DeltaEquivalency(
uint160 sqrtP,
uint160 sqrtQ,
uint128 liquidity,
bool roundUp
) external pure {
require(sqrtP >= sqrtQ);
require(sqrtP > 0 && sqrtQ > 0);
require((sqrtP * sqrtQ) / sqrtP == sqrtQ);
uint256 numerator1 = uint256(liquidity) << Constants.RESOLUTION;
uint256 numerator2 = sqrtP - sqrtQ;
uint256 denominator = uint256(sqrtP) * sqrtQ;
uint256 safeResult = roundUp
? FullMath.mulDivRoundingUp(numerator1, numerator2, denominator)
: FullMath.mulDiv(numerator1, numerator2, denominator);
uint256 fullResult = TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity, roundUp);
assert(safeResult == fullResult);
}
function getToken1DeltaInvariants(
uint160 sqrtP,
uint160 sqrtQ,
uint128 liquidity
) external pure {
require(sqrtP > 0 && sqrtQ > 0);
if (sqrtP > sqrtQ) (sqrtP, sqrtQ) = (sqrtQ, sqrtP);
uint256 amount1Down = TokenDeltaMath.getToken1Delta(sqrtP, sqrtQ, liquidity, false);
uint256 amount1Up = TokenDeltaMath.getToken1Delta(sqrtP, sqrtQ, liquidity, true);
assert(amount1Down <= amount1Up);
// diff is 0 or 1
assert(amount1Up - amount1Down < 2);
}
function getToken0DeltaSignedInvariants(
uint160 sqrtP,
uint160 sqrtQ,
int128 liquidity
) external pure {
require(sqrtP > 0 && sqrtQ > 0);
int256 amount0 = TokenDeltaMath.getToken0Delta(sqrtQ, sqrtP, liquidity);
if (liquidity < 0) assert(amount0 <= 0);
if (liquidity > 0) {
if (sqrtP == sqrtQ) assert(amount0 == 0);
else assert(amount0 > 0);
}
if (liquidity == 0) assert(amount0 == 0);
}
function getToken1DeltaSignedInvariants(
uint160 sqrtP,
uint160 sqrtQ,
int128 liquidity
) external pure {
require(sqrtP > 0 && sqrtQ > 0);
int256 amount1 = TokenDeltaMath.getToken1Delta(sqrtP, sqrtQ, liquidity);
if (liquidity < 0) assert(amount1 <= 0);
if (liquidity > 0) {
if (sqrtP == sqrtQ) assert(amount1 == 0);
else assert(amount1 > 0);
}
if (liquidity == 0) assert(amount1 == 0);
}
function getOutOfRangeMintInvariants(
uint160 sqrtA,
uint160 sqrtB,
int128 liquidity
) external pure {
require(sqrtA > 0 && sqrtB > 0);
require(liquidity > 0);
int256 amount0 = TokenDeltaMath.getToken0Delta(sqrtA, sqrtB, liquidity);
int256 amount1 = TokenDeltaMath.getToken1Delta(sqrtA, sqrtB, liquidity);
if (sqrtA == sqrtB) {
assert(amount0 == 0);
assert(amount1 == 0);
} else {
assert(amount0 > 0);
assert(amount1 > 0);
}
}
function getInRangeMintInvariants(
uint160 sqrtLower,
uint160 sqrtCurrent,
uint160 sqrtUpper,
int128 liquidity
) external pure {
require(sqrtLower > 0);
require(sqrtLower < sqrtUpper);
require(sqrtLower <= sqrtCurrent && sqrtCurrent <= sqrtUpper);
require(liquidity > 0);
int256 amount0 = TokenDeltaMath.getToken0Delta(sqrtCurrent, sqrtUpper, liquidity);
int256 amount1 = TokenDeltaMath.getToken1Delta(sqrtLower, sqrtCurrent, liquidity);
assert(amount0 > 0 || amount1 > 0);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './LowGasSafeMath.sol';
import './SafeCast.sol';
import './FullMath.sol';
import './Constants.sol';
/// @title Functions based on Q64.96 sqrt price and liquidity
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
library TokenDeltaMath {
using LowGasSafeMath for uint256;
using SafeCast for uint256;
/// @notice Gets the token0 delta between two prices
/// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper)
/// @param priceLower A Q64.96 sqrt price
/// @param priceUpper Another Q64.96 sqrt price
/// @param liquidity The amount of usable liquidity
/// @param roundUp Whether to round the amount up or down
/// @return token0Delta Amount of token0 required to cover a position of size liquidity between the two passed prices
function getToken0Delta(
uint160 priceLower,
uint160 priceUpper,
uint128 liquidity,
bool roundUp
) internal pure returns (uint256 token0Delta) {
uint256 priceDelta = priceUpper - priceLower;
require(priceDelta < priceUpper); // forbids underflow and 0 priceLower
uint256 liquidityShifted = uint256(liquidity) << Constants.RESOLUTION;
token0Delta = roundUp
? FullMath.divRoundingUp(FullMath.mulDivRoundingUp(priceDelta, liquidityShifted, priceUpper), priceLower)
: FullMath.mulDiv(priceDelta, liquidityShifted, priceUpper) / priceLower;
}
/// @notice Gets the token1 delta between two prices
/// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
/// @param priceLower A Q64.96 sqrt price
/// @param priceUpper Another Q64.96 sqrt price
/// @param liquidity The amount of usable liquidity
/// @param roundUp Whether to round the amount up, or down
/// @return token1Delta Amount of token1 required to cover a position of size liquidity between the two passed prices
function getToken1Delta(
uint160 priceLower,
uint160 priceUpper,
uint128 liquidity,
bool roundUp
) internal pure returns (uint256 token1Delta) {
require(priceUpper >= priceLower);
uint256 priceDelta = priceUpper - priceLower;
token1Delta = roundUp ? FullMath.mulDivRoundingUp(priceDelta, liquidity, Constants.Q96) : FullMath.mulDiv(priceDelta, liquidity, Constants.Q96);
}
/// @notice Helper that gets signed token0 delta
/// @param priceLower A Q64.96 sqrt price
/// @param priceUpper Another Q64.96 sqrt price
/// @param liquidity The change in liquidity for which to compute the token0 delta
/// @return token0Delta Amount of token0 corresponding to the passed liquidityDelta between the two prices
function getToken0Delta(
uint160 priceLower,
uint160 priceUpper,
int128 liquidity
) internal pure returns (int256 token0Delta) {
token0Delta = liquidity >= 0
? getToken0Delta(priceLower, priceUpper, uint128(liquidity), true).toInt256()
: -getToken0Delta(priceLower, priceUpper, uint128(-liquidity), false).toInt256();
}
/// @notice Helper that gets signed token1 delta
/// @param priceLower A Q64.96 sqrt price
/// @param priceUpper Another Q64.96 sqrt price
/// @param liquidity The change in liquidity for which to compute the token1 delta
/// @return token1Delta Amount of token1 corresponding to the passed liquidityDelta between the two prices
function getToken1Delta(
uint160 priceLower,
uint160 priceUpper,
int128 liquidity
) internal pure returns (int256 token1Delta) {
token1Delta = liquidity >= 0
? getToken1Delta(priceLower, priceUpper, uint128(liquidity), true).toInt256()
: -getToken1Delta(priceLower, priceUpper, uint128(-liquidity), false).toInt256();
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './FullMath.sol';
import './TokenDeltaMath.sol';
/// @title Computes the result of price movement
/// @notice Contains methods for computing the result of price movement within a single tick price range.
library PriceMovementMath {
using LowGasSafeMath for uint256;
using SafeCast for uint256;
/// @notice Gets the next sqrt price given an input amount of token0 or token1
/// @dev Throws if price or liquidity are 0, or if the next price is out of bounds
/// @param price The starting Q64.96 sqrt price, i.e., before accounting for the input amount
/// @param liquidity The amount of usable liquidity
/// @param input How much of token0, or token1, is being swapped in
/// @param zeroToOne Whether the amount in is token0 or token1
/// @return resultPrice The Q64.96 sqrt price after adding the input amount to token0 or token1
function getNewPriceAfterInput(
uint160 price,
uint128 liquidity,
uint256 input,
bool zeroToOne
) internal pure returns (uint160 resultPrice) {
return getNewPrice(price, liquidity, input, zeroToOne, true);
}
/// @notice Gets the next sqrt price given an output amount of token0 or token1
/// @dev Throws if price or liquidity are 0 or the next price is out of bounds
/// @param price The starting Q64.96 sqrt price before accounting for the output amount
/// @param liquidity The amount of usable liquidity
/// @param output How much of token0, or token1, is being swapped out
/// @param zeroToOne Whether the amount out is token0 or token1
/// @return resultPrice The Q64.96 sqrt price after removing the output amount of token0 or token1
function getNewPriceAfterOutput(
uint160 price,
uint128 liquidity,
uint256 output,
bool zeroToOne
) internal pure returns (uint160 resultPrice) {
return getNewPrice(price, liquidity, output, zeroToOne, false);
}
function getNewPrice(
uint160 price,
uint128 liquidity,
uint256 amount,
bool zeroToOne,
bool fromInput
) internal pure returns (uint160 resultPrice) {
require(price > 0);
require(liquidity > 0);
if (zeroToOne == fromInput) {
// rounding up or down
if (amount == 0) return price;
uint256 liquidityShifted = uint256(liquidity) << Constants.RESOLUTION;
if (fromInput) {
uint256 product;
if ((product = amount * price) / amount == price) {
uint256 denominator = liquidityShifted + product;
if (denominator >= liquidityShifted) return uint160(FullMath.mulDivRoundingUp(liquidityShifted, price, denominator)); // always fits in 160 bits
}
return uint160(FullMath.divRoundingUp(liquidityShifted, (liquidityShifted / price).add(amount)));
} else {
uint256 product;
require((product = amount * price) / amount == price); // if the product overflows, we know the denominator underflows
require(liquidityShifted > product); // in addition, we must check that the denominator does not underflow
return FullMath.mulDivRoundingUp(liquidityShifted, price, liquidityShifted - product).toUint160();
}
} else {
// if we're adding (subtracting), rounding down requires rounding the quotient down (up)
// in both cases, avoid a mulDiv for most inputs
if (fromInput) {
return
uint256(price)
.add(amount <= type(uint160).max ? (amount << Constants.RESOLUTION) / liquidity : FullMath.mulDiv(amount, Constants.Q96, liquidity))
.toUint160();
} else {
uint256 quotient = amount <= type(uint160).max
? FullMath.divRoundingUp(amount << Constants.RESOLUTION, liquidity)
: FullMath.mulDivRoundingUp(amount, Constants.Q96, liquidity);
require(price > quotient);
return uint160(price - quotient); // always fits 160 bits
}
}
}
function getTokenADelta01(
uint160 to,
uint160 from,
uint128 liquidity
) internal pure returns (uint256) {
return TokenDeltaMath.getToken0Delta(to, from, liquidity, true);
}
function getTokenADelta10(
uint160 to,
uint160 from,
uint128 liquidity
) internal pure returns (uint256) {
return TokenDeltaMath.getToken1Delta(from, to, liquidity, true);
}
function getTokenBDelta01(
uint160 to,
uint160 from,
uint128 liquidity
) internal pure returns (uint256) {
return TokenDeltaMath.getToken1Delta(to, from, liquidity, false);
}
function getTokenBDelta10(
uint160 to,
uint160 from,
uint128 liquidity
) internal pure returns (uint256) {
return TokenDeltaMath.getToken0Delta(from, to, liquidity, false);
}
/// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap
/// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive
/// @param currentPrice The current Q64.96 sqrt price of the pool
/// @param targetPrice The Q64.96 sqrt price that cannot be exceeded, from which the direction of the swap is inferred
/// @param liquidity The usable liquidity
/// @param amountAvailable How much input or output amount is remaining to be swapped in/out
/// @param fee The fee taken from the input amount, expressed in hundredths of a bip
/// @return resultPrice The Q64.96 sqrt price after swapping the amount in/out, not to exceed the price target
/// @return input The amount to be swapped in, of either token0 or token1, based on the direction of the swap
/// @return output The amount to be received, of either token0 or token1, based on the direction of the swap
/// @return feeAmount The amount of input that will be taken as a fee
function movePriceTowardsTarget(
bool zeroToOne,
uint160 currentPrice,
uint160 targetPrice,
uint128 liquidity,
int256 amountAvailable,
uint16 fee
)
internal
pure
returns (
uint160 resultPrice,
uint256 input,
uint256 output,
uint256 feeAmount
)
{
function(uint160, uint160, uint128) pure returns (uint256) getAmountA = zeroToOne ? getTokenADelta01 : getTokenADelta10;
if (amountAvailable >= 0) {
// exactIn or not
uint256 amountAvailableAfterFee = FullMath.mulDiv(uint256(amountAvailable), 1e6 - fee, 1e6);
input = getAmountA(targetPrice, currentPrice, liquidity);
if (amountAvailableAfterFee >= input) {
resultPrice = targetPrice;
feeAmount = FullMath.mulDivRoundingUp(input, fee, 1e6 - fee);
} else {
resultPrice = getNewPriceAfterInput(currentPrice, liquidity, amountAvailableAfterFee, zeroToOne);
if (targetPrice != resultPrice) {
input = getAmountA(resultPrice, currentPrice, liquidity);
// we didn't reach the target, so take the remainder of the maximum input as fee
feeAmount = uint256(amountAvailable) - input;
} else {
feeAmount = FullMath.mulDivRoundingUp(input, fee, 1e6 - fee);
}
}
output = (zeroToOne ? getTokenBDelta01 : getTokenBDelta10)(resultPrice, currentPrice, liquidity);
} else {
function(uint160, uint160, uint128) pure returns (uint256) getAmountB = zeroToOne ? getTokenBDelta01 : getTokenBDelta10;
output = getAmountB(targetPrice, currentPrice, liquidity);
amountAvailable = -amountAvailable;
if (uint256(amountAvailable) >= output) resultPrice = targetPrice;
else {
resultPrice = getNewPriceAfterOutput(currentPrice, liquidity, uint256(amountAvailable), zeroToOne);
if (targetPrice != resultPrice) {
output = getAmountB(resultPrice, currentPrice, liquidity);
}
// cap the output amount to not exceed the remaining output amount
if (output > uint256(amountAvailable)) {
output = uint256(amountAvailable);
}
}
input = getAmountA(resultPrice, currentPrice, liquidity);
feeAmount = FullMath.mulDivRoundingUp(input, fee, 1e6 - fee);
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.0;
/// @title Optimized overflow and underflow safe math operations
/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add128(uint128 x, uint128 y) internal pure returns (uint128 z) {
require((z = x + y) >= x);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library SafeCast {
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param y The uint256 to be downcasted
/// @return z The downcasted integer, now type uint160
function toUint160(uint256 y) internal pure returns (uint160 z) {
require((z = uint160(y)) == y);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param y The int256 to be downcasted
/// @return z The downcasted integer, now type int128
function toInt128(int256 y) internal pure returns (int128 z) {
require((z = int128(y)) == y);
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param y The uint256 to be casted
/// @return z The casted integer, now type int256
function toInt256(uint256 y) internal pure returns (int256 z) {
require(y < 2**255);
z = int256(y);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TokenDeltaMath.sol';
import '../libraries/PriceMovementMath.sol';
contract TokenDeltaMathTest {
function getNewPriceAfterInput(
uint160 sqrtP,
uint128 liquidity,
uint256 amountIn,
bool zeroToOne
) external pure returns (uint160 sqrtQ) {
return PriceMovementMath.getNewPriceAfterInput(sqrtP, liquidity, amountIn, zeroToOne);
}
function getGasCostOfGetNewPriceAfterInput(
uint160 sqrtP,
uint128 liquidity,
uint256 amountIn,
bool zeroToOne
) external view returns (uint256) {
uint256 gasBefore = gasleft();
PriceMovementMath.getNewPriceAfterInput(sqrtP, liquidity, amountIn, zeroToOne);
return gasBefore - gasleft();
}
function getNewPriceAfterOutput(
uint160 sqrtP,
uint128 liquidity,
uint256 amountOut,
bool zeroToOne
) external pure returns (uint160 sqrtQ) {
return PriceMovementMath.getNewPriceAfterOutput(sqrtP, liquidity, amountOut, zeroToOne);
}
function getGasCostOfGetNewPriceAfterOutput(
uint160 sqrtP,
uint128 liquidity,
uint256 amountOut,
bool zeroToOne
) external view returns (uint256) {
uint256 gasBefore = gasleft();
PriceMovementMath.getNewPriceAfterOutput(sqrtP, liquidity, amountOut, zeroToOne);
return gasBefore - gasleft();
}
function getToken0Delta(
uint160 sqrtLower,
uint160 sqrtUpper,
uint128 liquidity,
bool roundUp
) external pure returns (uint256 amount0) {
return TokenDeltaMath.getToken0Delta(sqrtLower, sqrtUpper, liquidity, roundUp);
}
function getToken1Delta(
uint160 sqrtLower,
uint160 sqrtUpper,
uint128 liquidity,
bool roundUp
) external pure returns (uint256 amount1) {
return TokenDeltaMath.getToken1Delta(sqrtLower, sqrtUpper, liquidity, roundUp);
}
function getGasCostOfGetToken0Delta(
uint160 sqrtLower,
uint160 sqrtUpper,
uint128 liquidity,
bool roundUp
) external view returns (uint256) {
uint256 gasBefore = gasleft();
TokenDeltaMath.getToken0Delta(sqrtLower, sqrtUpper, liquidity, roundUp);
return gasBefore - gasleft();
}
function getGasCostOfGetToken1Delta(
uint160 sqrtLower,
uint160 sqrtUpper,
uint128 liquidity,
bool roundUp
) external view returns (uint256) {
uint256 gasBefore = gasleft();
TokenDeltaMath.getToken1Delta(sqrtLower, sqrtUpper, liquidity, roundUp);
return gasBefore - gasleft();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/FullMath.sol';
contract UnsafeMathEchidnaTest {
function checkDivRoundingUp(uint256 x, uint256 d) external pure {
require(d > 0);
uint256 z = FullMath.divRoundingUp(x, d);
uint256 diff = z - (x / d);
if (x % d == 0) {
assert(diff == 0);
} else {
assert(diff == 1);
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '../libraries/FullMath.sol';
import '../libraries/Constants.sol';
/// @title Liquidity amount functions
/// @notice Provides functions for computing liquidity amounts from token amounts and prices
library LiquidityAmounts {
/// @notice Downcasts uint256 to uint128
/// @param x The uint258 to be downcasted
/// @return y The passed value, downcasted to uint128
function toUint128(uint256 x) private pure returns (uint128 y) {
require((y = uint128(x)) == x);
}
/// @notice Computes the amount of liquidity received for a given amount of token0 and price range
/// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount0 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount0(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0
) internal pure returns (uint128 liquidity) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, Constants.Q96);
return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96));
}
/// @notice Computes the amount of liquidity received for a given amount of token1 and price range
/// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)).
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount1 The amount1 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount1(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount1
) internal pure returns (uint128 liquidity) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return toUint128(FullMath.mulDiv(amount1, Constants.Q96, sqrtRatioBX96 - sqrtRatioAX96));
}
/// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount of token0 being sent in
/// @param amount1 The amount of token1 being sent in
/// @return liquidity The maximum amount of liquidity received
function getLiquidityForAmounts(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0,
uint256 amount1
) internal pure returns (uint128 liquidity) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
liquidity = getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
uint128 liquidity0 = getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0);
uint128 liquidity1 = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1);
liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
} else {
liquidity = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1);
}
}
/// @notice Computes the amount of token0 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
function getAmount0ForLiquidity(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return FullMath.mulDiv(uint256(liquidity) << Constants.RESOLUTION, sqrtRatioBX96 - sqrtRatioAX96, sqrtRatioBX96) / sqrtRatioAX96;
}
/// @notice Computes the amount of token1 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount1 The amount of token1
function getAmount1ForLiquidity(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount1) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, Constants.Q96);
}
/// @notice Computes the token0 and token1 value for a given amount of liquidity, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function getAmountsForLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0, uint256 amount1) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity);
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity);
} else {
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../interfaces/IERC20Minimal.sol';
import '../libraries/SafeCast.sol';
import '../libraries/TickMath.sol';
import '../interfaces/callback/IAlgebraMintCallback.sol';
import '../interfaces/callback/IAlgebraSwapCallback.sol';
import '../interfaces/callback/IAlgebraFlashCallback.sol';
import '../interfaces/IAlgebraPool.sol';
import './LiquidityAmounts.sol';
contract TestAlgebraCallee is IAlgebraMintCallback, IAlgebraSwapCallback, IAlgebraFlashCallback {
using SafeCast for uint256;
function swapExact0For1(
address pool,
uint256 amount0In,
address recipient,
uint160 limitSqrtPrice
) external {
IAlgebraPool(pool).swap(recipient, true, amount0In.toInt256(), limitSqrtPrice, abi.encode(msg.sender));
}
function swapExact0For1SupportingFee(
address pool,
uint256 amount0In,
address recipient,
uint160 limitSqrtPrice
) external {
IAlgebraPool(pool).swapSupportingFeeOnInputTokens(msg.sender, recipient, true, amount0In.toInt256(), limitSqrtPrice, abi.encode(msg.sender));
}
function swap0ForExact1(
address pool,
uint256 amount1Out,
address recipient,
uint160 limitSqrtPrice
) external {
IAlgebraPool(pool).swap(recipient, true, -amount1Out.toInt256(), limitSqrtPrice, abi.encode(msg.sender));
}
function swapExact1For0(
address pool,
uint256 amount1In,
address recipient,
uint160 limitSqrtPrice
) external {
IAlgebraPool(pool).swap(recipient, false, amount1In.toInt256(), limitSqrtPrice, abi.encode(msg.sender));
}
function swapExact1For0SupportingFee(
address pool,
uint256 amount1In,
address recipient,
uint160 limitSqrtPrice
) external {
IAlgebraPool(pool).swapSupportingFeeOnInputTokens(msg.sender, recipient, false, amount1In.toInt256(), limitSqrtPrice, abi.encode(msg.sender));
}
function swap1ForExact0(
address pool,
uint256 amount0Out,
address recipient,
uint160 limitSqrtPrice
) external {
IAlgebraPool(pool).swap(recipient, false, -amount0Out.toInt256(), limitSqrtPrice, abi.encode(msg.sender));
}
function swapToLowerSqrtPrice(
address pool,
uint160 price,
address recipient
) external {
IAlgebraPool(pool).swap(recipient, true, type(int256).max, price, abi.encode(msg.sender));
}
function swapToHigherSqrtPrice(
address pool,
uint160 price,
address recipient
) external {
IAlgebraPool(pool).swap(recipient, false, type(int256).max, price, abi.encode(msg.sender));
}
event SwapCallback(int256 amount0Delta, int256 amount1Delta);
function algebraSwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external override {
address sender = abi.decode(data, (address));
emit SwapCallback(amount0Delta, amount1Delta);
if (amount0Delta > 0) {
IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta));
} else if (amount1Delta > 0) {
IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta));
} else {
// if both are not gt 0, both must be 0.
assert(amount0Delta == 0 && amount1Delta == 0);
}
}
event MintResult(uint256 amount0Owed, uint256 amount1Owed, uint256 resultLiquidity);
function mint(
address pool,
address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount
)
external
returns (
uint256 amount0Owed,
uint256 amount1Owed,
uint256 resultLiquidity
)
{
(amount0Owed, amount1Owed, resultLiquidity) = IAlgebraPool(pool).mint(msg.sender, recipient, bottomTick, topTick, amount, abi.encode(msg.sender));
emit MintResult(amount0Owed, amount1Owed, resultLiquidity);
}
event MintCallback(uint256 amount0Owed, uint256 amount1Owed);
function algebraMintCallback(
uint256 amount0Owed,
uint256 amount1Owed,
bytes calldata data
) external override {
address sender = abi.decode(data, (address));
if (amount0Owed > 0) IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, amount0Owed);
if (amount1Owed > 0) IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, amount1Owed);
emit MintCallback(amount0Owed, amount1Owed);
}
event FlashCallback(uint256 fee0, uint256 fee1);
function flash(
address pool,
address recipient,
uint256 amount0,
uint256 amount1,
uint256 pay0,
uint256 pay1
) external {
IAlgebraPool(pool).flash(recipient, amount0, amount1, abi.encode(msg.sender, pay0, pay1));
}
function algebraFlashCallback(
uint256 fee0,
uint256 fee1,
bytes calldata data
) external override {
emit FlashCallback(fee0, fee1);
(address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256));
if (pay0 > 0) IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, pay0);
if (pay1 > 0) IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, pay1);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Minimal ERC20 interface for Algebra
/// @notice Contains a subset of the full ERC20 interface that is used in Algebra
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IERC20Minimal {
/// @notice Returns the balance of a token
/// @param account The account for which to look up the number of tokens it has, i.e. its balance
/// @return The number of tokens held by the account
function balanceOf(address account) external view returns (uint256);
/// @notice Transfers the amount of token from the `msg.sender` to the recipient
/// @param recipient The account that will receive the amount transferred
/// @param amount The number of tokens to send from the sender to the recipient
/// @return Returns true for a successful transfer, false for an unsuccessful transfer
function transfer(address recipient, uint256 amount) external returns (bool);
/// @notice Returns the current allowance given to a spender by an owner
/// @param owner The account of the token owner
/// @param spender The account of the token spender
/// @return The current allowance granted by `owner` to `spender`
function allowance(address owner, address spender) external view returns (uint256);
/// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount`
/// @param spender The account which will be allowed to spend a given amount of the owners tokens
/// @param amount The amount of tokens allowed to be used by `spender`
/// @return Returns true for a successful approval, false for unsuccessful
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender`
/// @param sender The account from which the transfer will be initiated
/// @param recipient The recipient of the transfer
/// @param amount The amount of the transfer
/// @return Returns true for a successful transfer, false for unsuccessful
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`.
/// @param from The account from which the tokens were sent, i.e. the balance decreased
/// @param to The account to which the tokens were sent, i.e. the balance increased
/// @param value The amount of tokens that were transferred
event Transfer(address indexed from, address indexed to, uint256 value);
/// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes.
/// @param owner The account that approved spending of its tokens
/// @param spender The account for which the spending allowance was modified
/// @param value The new allowance from the owner to the spender
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library TickMath {
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
int24 internal constant MAX_TICK = -MIN_TICK;
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
/// @notice Calculates sqrt(1.0001^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return price A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
/// at the given tick
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 price) {
// get abs value
int24 mask = tick >> (24 - 1);
uint256 absTick = uint256((tick ^ mask) - mask);
require(absTick <= uint256(MAX_TICK), 'T');
uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
if (tick > 0) ratio = type(uint256).max / ratio;
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint
// we round up in the division so getTickAtSqrtRatio of the output price is always consistent
price = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
}
/// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
/// @dev Throws in case price < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
/// ever return.
/// @param price The sqrt ratio for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
function getTickAtSqrtRatio(uint160 price) internal pure returns (int24 tick) {
// second inequality must be < because the price can never reach the price at the max tick
require(price >= MIN_SQRT_RATIO && price < MAX_SQRT_RATIO, 'R');
uint256 ratio = uint256(price) << 32;
uint256 r = ratio;
uint256 msb = 0;
assembly {
let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(5, gt(r, 0xFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(4, gt(r, 0xFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(3, gt(r, 0xFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(2, gt(r, 0xF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(1, gt(r, 0x3))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := gt(r, 0x1)
msb := or(msb, f)
}
if (msb >= 128) r = ratio >> (msb - 127);
else r = ratio << (127 - msb);
int256 log_2 = (int256(msb) - 128) << 64;
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(63, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(62, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(61, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(60, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(59, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(58, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(57, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(56, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(55, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(54, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(53, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(52, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(51, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(50, f))
}
int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number
int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);
tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= price ? tickHi : tickLow;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IAlgebraPoolActions#mint
/// @notice Any contract that calls IAlgebraPoolActions#mint must implement this interface
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraMintCallback {
/// @notice Called to `msg.sender` after minting liquidity to a position from IAlgebraPool#mint.
/// @dev In the implementation you must pay the pool tokens owed for the minted liquidity.
/// The caller of this method must be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
/// @param amount0Owed The amount of token0 due to the pool for the minted liquidity
/// @param amount1Owed The amount of token1 due to the pool for the minted liquidity
/// @param data Any data passed through by the caller via the IAlgebraPoolActions#mint call
function algebraMintCallback(
uint256 amount0Owed,
uint256 amount1Owed,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IAlgebraPoolActions#swap
/// @notice Any contract that calls IAlgebraPoolActions#swap must implement this interface
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraSwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IAlgebraPool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IAlgebraPoolActions#swap call
function algebraSwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title Callback for IAlgebraPoolActions#flash
* @notice Any contract that calls IAlgebraPoolActions#flash must implement this interface
* @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraFlashCallback {
/**
* @notice Called to `msg.sender` after transferring to the recipient from IAlgebraPool#flash.
* @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts.
* The caller of this method must be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
* @param fee0 The fee amount in token0 due to the pool by the end of the flash
* @param fee1 The fee amount in token1 due to the pool by the end of the flash
* @param data Any data passed through by the caller via the IAlgebraPoolActions#flash call
*/
function algebraFlashCallback(
uint256 fee0,
uint256 fee1,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import './pool/IAlgebraPoolImmutables.sol';
import './pool/IAlgebraPoolState.sol';
import './pool/IAlgebraPoolDerivedState.sol';
import './pool/IAlgebraPoolActions.sol';
import './pool/IAlgebraPoolPermissionedActions.sol';
import './pool/IAlgebraPoolEvents.sol';
/**
* @title The interface for a Algebra Pool
* @dev The pool interface is broken up into many smaller pieces.
* Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraPool is
IAlgebraPoolImmutables,
IAlgebraPoolState,
IAlgebraPoolDerivedState,
IAlgebraPoolActions,
IAlgebraPoolPermissionedActions,
IAlgebraPoolEvents
{
// used only for combining interfaces
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '../IDataStorageOperator.sol';
/// @title Pool state that never changes
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolImmutables {
/**
* @notice The contract that stores all the timepoints and can perform actions with them
* @return The operator address
*/
function dataStorageOperator() external view returns (address);
/**
* @notice The contract that deployed the pool, which must adhere to the IAlgebraFactory interface
* @return The contract address
*/
function factory() external view returns (address);
/**
* @notice The first of the two tokens of the pool, sorted by address
* @return The token contract address
*/
function token0() external view returns (address);
/**
* @notice The second of the two tokens of the pool, sorted by address
* @return The token contract address
*/
function token1() external view returns (address);
/**
* @notice The pool tick spacing
* @dev Ticks can only be used at multiples of this value
* e.g.: a tickSpacing of 60 means ticks can be initialized every 60th tick, i.e., ..., -120, -60, 0, 60, 120, ...
* This value is an int24 to avoid casting even though it is always positive.
* @return The tick spacing
*/
function tickSpacing() external view returns (int24);
/**
* @notice The maximum amount of position liquidity that can use any tick in the range
* @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
* also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
* @return The max amount of liquidity per tick
*/
function maxLiquidityPerTick() external view returns (uint128);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Pool state that can change
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolState {
/**
* @notice The globalState structure in the pool stores many values but requires only one slot
* and is exposed as a single method to save gas when accessed externally.
* @return price The current price of the pool as a sqrt(token1/token0) Q64.96 value;
* Returns tick The current tick of the pool, i.e. according to the last tick transition that was run;
* Returns This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(price) if the price is on a tick
* boundary;
* Returns fee The last pool fee value in hundredths of a bip, i.e. 1e-6;
* Returns timepointIndex The index of the last written timepoint;
* Returns communityFeeToken0 The community fee percentage of the swap fee in thousandths (1e-3) for token0;
* Returns communityFeeToken1 The community fee percentage of the swap fee in thousandths (1e-3) for token1;
* Returns unlocked Whether the pool is currently locked to reentrancy;
*/
function globalState()
external
view
returns (
uint160 price,
int24 tick,
uint16 fee,
uint16 timepointIndex,
uint8 communityFeeToken0,
uint8 communityFeeToken1,
bool unlocked
);
/**
* @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
* @dev This value can overflow the uint256
*/
function totalFeeGrowth0Token() external view returns (uint256);
/**
* @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
* @dev This value can overflow the uint256
*/
function totalFeeGrowth1Token() external view returns (uint256);
/**
* @notice The currently in range liquidity available to the pool
* @dev This value has no relationship to the total liquidity across all ticks.
* Returned value cannot exceed type(uint128).max
*/
function liquidity() external view returns (uint128);
/**
* @notice Look up information about a specific tick in the pool
* @dev This is a public structure, so the `return` natspec tags are omitted.
* @param tick The tick to look up
* @return liquidityTotal the total amount of position liquidity that uses the pool either as tick lower or
* tick upper;
* Returns liquidityDelta how much liquidity changes when the pool price crosses the tick;
* Returns outerFeeGrowth0Token the fee growth on the other side of the tick from the current tick in token0;
* Returns outerFeeGrowth1Token the fee growth on the other side of the tick from the current tick in token1;
* Returns outerTickCumulative the cumulative tick value on the other side of the tick from the current tick;
* Returns outerSecondsPerLiquidity the seconds spent per liquidity on the other side of the tick from the current tick;
* Returns outerSecondsSpent the seconds spent on the other side of the tick from the current tick;
* Returns initialized Set to true if the tick is initialized, i.e. liquidityTotal is greater than 0
* otherwise equal to false. Outside values can only be used if the tick is initialized.
* In addition, these values are only relative and must be used only in comparison to previous snapshots for
* a specific position.
*/
function ticks(int24 tick)
external
view
returns (
uint128 liquidityTotal,
int128 liquidityDelta,
uint256 outerFeeGrowth0Token,
uint256 outerFeeGrowth1Token,
int56 outerTickCumulative,
uint160 outerSecondsPerLiquidity,
uint32 outerSecondsSpent,
bool initialized
);
/** @notice Returns 256 packed tick initialized boolean values. See TickTable for more information */
function tickTable(int16 wordPosition) external view returns (uint256);
/**
* @notice Returns the information about a position by the position's key
* @dev This is a public mapping of structures, so the `return` natspec tags are omitted.
* @param key The position's key is a hash of a preimage composed by the owner, bottomTick and topTick
* @return liquidityAmount The amount of liquidity in the position;
* Returns lastLiquidityAddTimestamp Timestamp of last adding of liquidity;
* Returns innerFeeGrowth0Token Fee growth of token0 inside the tick range as of the last mint/burn/poke;
* Returns innerFeeGrowth1Token Fee growth of token1 inside the tick range as of the last mint/burn/poke;
* Returns fees0 The computed amount of token0 owed to the position as of the last mint/burn/poke;
* Returns fees1 The computed amount of token1 owed to the position as of the last mint/burn/poke
*/
function positions(bytes32 key)
external
view
returns (
uint128 liquidityAmount,
uint32 lastLiquidityAddTimestamp,
uint256 innerFeeGrowth0Token,
uint256 innerFeeGrowth1Token,
uint128 fees0,
uint128 fees1
);
/**
* @notice Returns data about a specific timepoint index
* @param index The element of the timepoints array to fetch
* @dev You most likely want to use #getTimepoints() instead of this method to get an timepoint as of some amount of time
* ago, rather than at a specific index in the array.
* This is a public mapping of structures, so the `return` natspec tags are omitted.
* @return initialized whether the timepoint has been initialized and the values are safe to use;
* Returns blockTimestamp The timestamp of the timepoint;
* Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the timepoint timestamp;
* Returns secondsPerLiquidityCumulative the seconds per in range liquidity for the life of the pool as of the timepoint timestamp;
* Returns volatilityCumulative Cumulative standard deviation for the life of the pool as of the timepoint timestamp;
* Returns averageTick Time-weighted average tick;
* Returns volumePerLiquidityCumulative Cumulative swap volume per liquidity for the life of the pool as of the timepoint timestamp;
*/
function timepoints(uint256 index)
external
view
returns (
bool initialized,
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint88 volatilityCumulative,
int24 averageTick,
uint144 volumePerLiquidityCumulative
);
/**
* @notice Returns the information about active incentive
* @dev if there is no active incentive at the moment, virtualPool,endTimestamp,startTimestamp would be equal to 0
* @return virtualPool The address of a virtual pool associated with the current active incentive
*/
function activeIncentive() external view returns (address virtualPool);
/**
* @notice Returns the lock time for added liquidity
*/
function liquidityCooldown() external view returns (uint32 cooldownInSeconds);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title Pool state that is not stored
* @notice Contains view functions to provide information about the pool that is computed rather than stored on the
* blockchain. The functions here may have variable gas costs.
* @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraPoolDerivedState {
/**
* @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
* @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
* the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
* you must call it with secondsAgos = [3600, 0].
* @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
* log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
* @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
* @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
* @return secondsPerLiquidityCumulatives Cumulative seconds per liquidity-in-range value as of each `secondsAgos`
* from the current block timestamp
* @return volatilityCumulatives Cumulative standard deviation as of each `secondsAgos`
* @return volumePerAvgLiquiditys Cumulative swap volume per liquidity as of each `secondsAgos`
*/
function getTimepoints(uint32[] calldata secondsAgos)
external
view
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
);
/**
* @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
* @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
* I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
* snapshot is taken and the second snapshot is taken.
* @param bottomTick The lower tick of the range
* @param topTick The upper tick of the range
* @return innerTickCumulative The snapshot of the tick accumulator for the range
* @return innerSecondsSpentPerLiquidity The snapshot of seconds per liquidity for the range
* @return innerSecondsSpent The snapshot of the number of seconds during which the price was in this range
*/
function getInnerCumulatives(int24 bottomTick, int24 topTick)
external
view
returns (
int56 innerTickCumulative,
uint160 innerSecondsSpentPerLiquidity,
uint32 innerSecondsSpent
);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Permissionless pool actions
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolActions {
/**
* @notice Sets the initial price for the pool
* @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
* @param price the initial sqrt price of the pool as a Q64.96
*/
function initialize(uint160 price) external;
/**
* @notice Adds liquidity for the given recipient/bottomTick/topTick position
* @dev The caller of this method receives a callback in the form of IAlgebraMintCallback# AlgebraMintCallback
* in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
* on bottomTick, topTick, the amount of liquidity, and the current price.
* @param sender The address which will receive potential surplus of paid tokens
* @param recipient The address for which the liquidity will be created
* @param bottomTick The lower tick of the position in which to add liquidity
* @param topTick The upper tick of the position in which to add liquidity
* @param amount The desired amount of liquidity to mint
* @param data Any data that should be passed through to the callback
* @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
* @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
* @return liquidityActual The actual minted amount of liquidity
*/
function mint(
address sender,
address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount,
bytes calldata data
)
external
returns (
uint256 amount0,
uint256 amount1,
uint128 liquidityActual
);
/**
* @notice Collects tokens owed to a position
* @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
* Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
* amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
* actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
* @param recipient The address which should receive the fees collected
* @param bottomTick The lower tick of the position for which to collect fees
* @param topTick The upper tick of the position for which to collect fees
* @param amount0Requested How much token0 should be withdrawn from the fees owed
* @param amount1Requested How much token1 should be withdrawn from the fees owed
* @return amount0 The amount of fees collected in token0
* @return amount1 The amount of fees collected in token1
*/
function collect(
address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
/**
* @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
* @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
* @dev Fees must be collected separately via a call to #collect
* @param bottomTick The lower tick of the position for which to burn liquidity
* @param topTick The upper tick of the position for which to burn liquidity
* @param amount How much liquidity to burn
* @return amount0 The amount of token0 sent to the recipient
* @return amount1 The amount of token1 sent to the recipient
*/
function burn(
int24 bottomTick,
int24 topTick,
uint128 amount
) external returns (uint256 amount0, uint256 amount1);
/**
* @notice Swap token0 for token1, or token1 for token0
* @dev The caller of this method receives a callback in the form of IAlgebraSwapCallback# AlgebraSwapCallback
* @param recipient The address to receive the output of the swap
* @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
* @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
* @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
* value after the swap. If one for zero, the price cannot be greater than this value after the swap
* @param data Any data to be passed through to the callback. If using the Router it should contain
* SwapRouter#SwapCallbackData
* @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
* @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
*/
function swap(
address recipient,
bool zeroToOne,
int256 amountSpecified,
uint160 limitSqrtPrice,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
/**
* @notice Swap token0 for token1, or token1 for token0 (tokens that have fee on transfer)
* @dev The caller of this method receives a callback in the form of I AlgebraSwapCallback# AlgebraSwapCallback
* @param sender The address called this function (Comes from the Router)
* @param recipient The address to receive the output of the swap
* @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
* @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
* @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
* value after the swap. If one for zero, the price cannot be greater than this value after the swap
* @param data Any data to be passed through to the callback. If using the Router it should contain
* SwapRouter#SwapCallbackData
* @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
* @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
*/
function swapSupportingFeeOnInputTokens(
address sender,
address recipient,
bool zeroToOne,
int256 amountSpecified,
uint160 limitSqrtPrice,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
/**
* @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
* @dev The caller of this method receives a callback in the form of IAlgebraFlashCallback# AlgebraFlashCallback
* @dev All excess tokens paid in the callback are distributed to liquidity providers as an additional fee. So this method can be used
* to donate underlying tokens to currently in-range liquidity providers by calling with 0 amount{0,1} and sending
* the donation amount(s) from the callback
* @param recipient The address which will receive the token0 and token1 amounts
* @param amount0 The amount of token0 to send
* @param amount1 The amount of token1 to send
* @param data Any data to be passed through to the callback
*/
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title Permissioned pool actions
* @notice Contains pool methods that may only be called by the factory owner or tokenomics
* @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraPoolPermissionedActions {
/**
* @notice Set the community's % share of the fees. Cannot exceed 25% (250)
* @param communityFee0 new community fee percent for token0 of the pool in thousandths (1e-3)
* @param communityFee1 new community fee percent for token1 of the pool in thousandths (1e-3)
*/
function setCommunityFee(uint8 communityFee0, uint8 communityFee1) external;
/**
* @notice Sets an active incentive
* @param virtualPoolAddress The address of a virtual pool associated with the incentive
*/
function setIncentive(address virtualPoolAddress) external;
/**
* @notice Sets new lock time for added liquidity
* @param newLiquidityCooldown The time in seconds
*/
function setLiquidityCooldown(uint32 newLiquidityCooldown) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Events emitted by a pool
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolEvents {
/**
* @notice Emitted exactly once by a pool when #initialize is first called on the pool
* @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
* @param price The initial sqrt price of the pool, as a Q64.96
* @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
*/
event Initialize(uint160 price, int24 tick);
/**
* @notice Emitted when liquidity is minted for a given position
* @param sender The address that minted the liquidity
* @param owner The owner of the position and recipient of any minted liquidity
* @param bottomTick The lower tick of the position
* @param topTick The upper tick of the position
* @param liquidityAmount The amount of liquidity minted to the position range
* @param amount0 How much token0 was required for the minted liquidity
* @param amount1 How much token1 was required for the minted liquidity
*/
event Mint(
address sender,
address indexed owner,
int24 indexed bottomTick,
int24 indexed topTick,
uint128 liquidityAmount,
uint256 amount0,
uint256 amount1
);
/**
* @notice Emitted when fees are collected by the owner of a position
* @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
* @param owner The owner of the position for which fees are collected
* @param recipient The address that received fees
* @param bottomTick The lower tick of the position
* @param topTick The upper tick of the position
* @param amount0 The amount of token0 fees collected
* @param amount1 The amount of token1 fees collected
*/
event Collect(address indexed owner, address recipient, int24 indexed bottomTick, int24 indexed topTick, uint128 amount0, uint128 amount1);
/**
* @notice Emitted when a position's liquidity is removed
* @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
* @param owner The owner of the position for which liquidity is removed
* @param bottomTick The lower tick of the position
* @param topTick The upper tick of the position
* @param liquidityAmount The amount of liquidity to remove
* @param amount0 The amount of token0 withdrawn
* @param amount1 The amount of token1 withdrawn
*/
event Burn(address indexed owner, int24 indexed bottomTick, int24 indexed topTick, uint128 liquidityAmount, uint256 amount0, uint256 amount1);
/**
* @notice Emitted by the pool for any swaps between token0 and token1
* @param sender The address that initiated the swap call, and that received the callback
* @param recipient The address that received the output of the swap
* @param amount0 The delta of the token0 balance of the pool
* @param amount1 The delta of the token1 balance of the pool
* @param price The sqrt(price) of the pool after the swap, as a Q64.96
* @param liquidity The liquidity of the pool after the swap
* @param tick The log base 1.0001 of price of the pool after the swap
*/
event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 price, uint128 liquidity, int24 tick);
/**
* @notice Emitted by the pool for any flashes of token0/token1
* @param sender The address that initiated the swap call, and that received the callback
* @param recipient The address that received the tokens from flash
* @param amount0 The amount of token0 that was flashed
* @param amount1 The amount of token1 that was flashed
* @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
* @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
*/
event Flash(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1);
/**
* @notice Emitted when the community fee is changed by the pool
* @param communityFee0New The updated value of the token0 community fee percent
* @param communityFee1New The updated value of the token1 community fee percent
*/
event CommunityFee(uint8 communityFee0New, uint8 communityFee1New);
/**
* @notice Emitted when new activeIncentive is set
* @param virtualPoolAddress The address of a virtual pool associated with the current active incentive
*/
event Incentive(address indexed virtualPoolAddress);
/**
* @notice Emitted when the fee changes
* @param fee The value of the token fee
*/
event Fee(uint16 fee);
/**
* @notice Emitted when the LiquidityCooldown changes
* @param liquidityCooldown The value of locktime for added liquidity
*/
event LiquidityCooldown(uint32 liquidityCooldown);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../interfaces/IERC20Minimal.sol';
import '../interfaces/callback/IAlgebraSwapCallback.sol';
import '../interfaces/callback/IAlgebraMintCallback.sol';
import '../interfaces/IAlgebraPool.sol';
contract TestAlgebraSwapPay is IAlgebraSwapCallback, IAlgebraMintCallback {
function swap(
address pool,
address recipient,
bool zeroToOne,
uint160 price,
int256 amountSpecified,
uint256 pay0,
uint256 pay1
) external {
IAlgebraPool(pool).swap(recipient, zeroToOne, amountSpecified, price, abi.encode(msg.sender, pay0, pay1));
}
function swapSupportingFee(
address pool,
address recipient,
bool zeroToOne,
uint160 price,
int256 amountSpecified,
uint256 pay0,
uint256 pay1
) external {
IAlgebraPool(pool).swapSupportingFeeOnInputTokens(msg.sender, recipient, zeroToOne, amountSpecified, price, abi.encode(msg.sender, pay0, pay1));
}
function algebraSwapCallback(
int256,
int256,
bytes calldata data
) external override {
(address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256));
if (pay0 > 0) {
IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(pay0));
} else if (pay1 > 0) {
IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(pay1));
}
}
function mint(
address pool,
address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount,
uint256 pay0,
uint256 pay1
)
external
returns (
uint256 amount0Owed,
uint256 amount1Owed,
uint256 resultLiquidity
)
{
(amount0Owed, amount1Owed, resultLiquidity) = IAlgebraPool(pool).mint(
msg.sender,
recipient,
bottomTick,
topTick,
amount,
abi.encode(msg.sender, pay0, pay1)
);
}
function algebraMintCallback(
uint256 amount0Owed,
uint256 amount1Owed,
bytes calldata data
) external override {
(address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256));
if (amount0Owed > 0) IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, pay0);
if (amount1Owed > 0) IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, pay1);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
interface IAlgebraVirtualPool {
enum Status {
NOT_EXIST,
ACTIVE,
NOT_STARTED
}
/**
* @dev This function is called by the main pool when an initialized tick is crossed there.
* If the tick is also initialized in a virtual pool it should be crossed too
* @param nextTick The crossed tick
* @param zeroToOne The direction
*/
function cross(int24 nextTick, bool zeroToOne) external;
/**
* @dev This function is called from the main pool before every swap To increase seconds per liquidity
* cumulative considering previous timestamp and liquidity. The liquidity is stored in a virtual pool
* @param currentTimestamp The timestamp of the current swap
* @return Status The status of virtual pool
*/
function increaseCumulative(uint32 currentTimestamp) external returns (Status);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import '../interfaces/pool/IAlgebraPoolState.sol';
import '../libraries/TickManager.sol';
abstract contract PoolState is IAlgebraPoolState {
struct GlobalState {
uint160 price; // The square root of the current price in Q64.96 format
int24 tick; // The current tick
uint16 fee; // The current fee in hundredths of a bip, i.e. 1e-6
uint16 timepointIndex; // The index of the last written timepoint
uint8 communityFeeToken0; // The community fee represented as a percent of all collected fee in thousandths (1e-3)
uint8 communityFeeToken1;
bool unlocked; // True if the contract is unlocked, otherwise - false
}
/// @inheritdoc IAlgebraPoolState
uint256 public override totalFeeGrowth0Token;
/// @inheritdoc IAlgebraPoolState
uint256 public override totalFeeGrowth1Token;
/// @inheritdoc IAlgebraPoolState
GlobalState public override globalState;
/// @inheritdoc IAlgebraPoolState
uint128 public override liquidity;
uint128 internal volumePerLiquidityInBlock;
/// @inheritdoc IAlgebraPoolState
uint32 public override liquidityCooldown;
/// @inheritdoc IAlgebraPoolState
address public override activeIncentive;
/// @inheritdoc IAlgebraPoolState
mapping(int24 => TickManager.Tick) public override ticks;
/// @inheritdoc IAlgebraPoolState
mapping(int16 => uint256) public override tickTable;
/// @dev Reentrancy protection. Implemented in every function of the contract since there are checks of balances.
modifier lock() {
require(globalState.unlocked, 'LOK');
globalState.unlocked = false;
_;
globalState.unlocked = true;
}
/// @dev This function is created for testing by overriding it.
/// @return A timestamp converted to uint32
function _blockTimestamp() internal view virtual returns (uint32) {
return uint32(block.timestamp); // truncation is desired
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import '../interfaces/pool/IAlgebraPoolImmutables.sol';
import '../interfaces/IAlgebraPoolDeployer.sol';
import '../libraries/Constants.sol';
abstract contract PoolImmutables is IAlgebraPoolImmutables {
/// @inheritdoc IAlgebraPoolImmutables
address public immutable override dataStorageOperator;
/// @inheritdoc IAlgebraPoolImmutables
address public immutable override factory;
/// @inheritdoc IAlgebraPoolImmutables
address public immutable override token0;
/// @inheritdoc IAlgebraPoolImmutables
address public immutable override token1;
/// @inheritdoc IAlgebraPoolImmutables
function tickSpacing() external pure override returns (int24) {
return Constants.TICK_SPACING;
}
/// @inheritdoc IAlgebraPoolImmutables
function maxLiquidityPerTick() external pure override returns (uint128) {
return Constants.MAX_LIQUIDITY_PER_TICK;
}
constructor(address deployer) {
(dataStorageOperator, factory, token0, token1) = IAlgebraPoolDeployer(deployer).parameters();
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './LowGasSafeMath.sol';
import './SafeCast.sol';
import './LiquidityMath.sol';
import './Constants.sol';
/// @title TickManager
/// @notice Contains functions for managing tick processes and relevant calculations
library TickManager {
using LowGasSafeMath for int256;
using SafeCast for int256;
// info stored for each initialized individual tick
struct Tick {
uint128 liquidityTotal; // the total position liquidity that references this tick
int128 liquidityDelta; // amount of net liquidity added (subtracted) when tick is crossed left-right (right-left),
// fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
// only has relative meaning, not absolute — the value depends on when the tick is initialized
uint256 outerFeeGrowth0Token;
uint256 outerFeeGrowth1Token;
int56 outerTickCumulative; // the cumulative tick value on the other side of the tick
uint160 outerSecondsPerLiquidity; // the seconds per unit of liquidity on the _other_ side of current tick, (relative meaning)
uint32 outerSecondsSpent; // the seconds spent on the other side of the current tick, only has relative meaning
bool initialized; // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks
}
/// @notice Retrieves fee growth data
/// @param self The mapping containing all tick information for initialized ticks
/// @param bottomTick The lower tick boundary of the position
/// @param topTick The upper tick boundary of the position
/// @param currentTick The current tick
/// @param totalFeeGrowth0Token The all-time global fee growth, per unit of liquidity, in token0
/// @param totalFeeGrowth1Token The all-time global fee growth, per unit of liquidity, in token1
/// @return innerFeeGrowth0Token The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
/// @return innerFeeGrowth1Token The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
function getInnerFeeGrowth(
mapping(int24 => Tick) storage self,
int24 bottomTick,
int24 topTick,
int24 currentTick,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token
) internal view returns (uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token) {
Tick storage lower = self[bottomTick];
Tick storage upper = self[topTick];
if (currentTick < topTick) {
if (currentTick >= bottomTick) {
innerFeeGrowth0Token = totalFeeGrowth0Token - lower.outerFeeGrowth0Token;
innerFeeGrowth1Token = totalFeeGrowth1Token - lower.outerFeeGrowth1Token;
} else {
innerFeeGrowth0Token = lower.outerFeeGrowth0Token;
innerFeeGrowth1Token = lower.outerFeeGrowth1Token;
}
innerFeeGrowth0Token -= upper.outerFeeGrowth0Token;
innerFeeGrowth1Token -= upper.outerFeeGrowth1Token;
} else {
innerFeeGrowth0Token = upper.outerFeeGrowth0Token - lower.outerFeeGrowth0Token;
innerFeeGrowth1Token = upper.outerFeeGrowth1Token - lower.outerFeeGrowth1Token;
}
}
/// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa
/// @param self The mapping containing all tick information for initialized ticks
/// @param tick The tick that will be updated
/// @param currentTick The current tick
/// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left)
/// @param totalFeeGrowth0Token The all-time global fee growth, per unit of liquidity, in token0
/// @param totalFeeGrowth1Token The all-time global fee growth, per unit of liquidity, in token1
/// @param secondsPerLiquidityCumulative The all-time seconds per max(1, liquidity) of the pool
/// @param tickCumulative The all-time global cumulative tick
/// @param time The current block timestamp cast to a uint32
/// @param upper true for updating a position's upper tick, or false for updating a position's lower tick
/// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa
function update(
mapping(int24 => Tick) storage self,
int24 tick,
int24 currentTick,
int128 liquidityDelta,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token,
uint160 secondsPerLiquidityCumulative,
int56 tickCumulative,
uint32 time,
bool upper
) internal returns (bool flipped) {
Tick storage data = self[tick];
int128 liquidityDeltaBefore = data.liquidityDelta;
uint128 liquidityTotalBefore = data.liquidityTotal;
uint128 liquidityTotalAfter = LiquidityMath.addDelta(liquidityTotalBefore, liquidityDelta);
require(liquidityTotalAfter < Constants.MAX_LIQUIDITY_PER_TICK + 1, 'LO');
// when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed)
data.liquidityDelta = upper
? int256(liquidityDeltaBefore).sub(liquidityDelta).toInt128()
: int256(liquidityDeltaBefore).add(liquidityDelta).toInt128();
data.liquidityTotal = liquidityTotalAfter;
flipped = (liquidityTotalAfter == 0);
if (liquidityTotalBefore == 0) {
flipped = !flipped;
// by convention, we assume that all growth before a tick was initialized happened _below_ the tick
if (tick <= currentTick) {
data.outerFeeGrowth0Token = totalFeeGrowth0Token;
data.outerFeeGrowth1Token = totalFeeGrowth1Token;
data.outerSecondsPerLiquidity = secondsPerLiquidityCumulative;
data.outerTickCumulative = tickCumulative;
data.outerSecondsSpent = time;
}
data.initialized = true;
}
}
/// @notice Transitions to next tick as needed by price movement
/// @param self The mapping containing all tick information for initialized ticks
/// @param tick The destination tick of the transition
/// @param totalFeeGrowth0Token The all-time global fee growth, per unit of liquidity, in token0
/// @param totalFeeGrowth1Token The all-time global fee growth, per unit of liquidity, in token1
/// @param secondsPerLiquidityCumulative The current seconds per liquidity
/// @param tickCumulative The all-time global cumulative tick
/// @param time The current block.timestamp
/// @return liquidityDelta The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left)
function cross(
mapping(int24 => Tick) storage self,
int24 tick,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token,
uint160 secondsPerLiquidityCumulative,
int56 tickCumulative,
uint32 time
) internal returns (int128 liquidityDelta) {
Tick storage data = self[tick];
data.outerSecondsSpent = time - data.outerSecondsSpent;
data.outerSecondsPerLiquidity = secondsPerLiquidityCumulative - data.outerSecondsPerLiquidity;
data.outerTickCumulative = tickCumulative - data.outerTickCumulative;
data.outerFeeGrowth1Token = totalFeeGrowth1Token - data.outerFeeGrowth1Token;
data.outerFeeGrowth0Token = totalFeeGrowth0Token - data.outerFeeGrowth0Token;
return data.liquidityDelta;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './Constants.sol';
import './TickMath.sol';
/// @title Packed tick initialized state library
/// @notice Stores a packed mapping of tick index to its initialized state
/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.
library TickTable {
/// @notice Toggles the initialized state for a given tick from false to true, or vice versa
/// @param self The mapping in which to toggle the tick
/// @param tick The tick to toggle
function toggleTick(mapping(int16 => uint256) storage self, int24 tick) internal {
require(tick % Constants.TICK_SPACING == 0, 'tick is not spaced'); // ensure that the tick is spaced
tick /= Constants.TICK_SPACING; // compress tick
int16 rowNumber;
uint8 bitNumber;
assembly {
bitNumber := and(tick, 0xFF)
rowNumber := shr(8, tick)
}
self[rowNumber] ^= 1 << bitNumber;
}
/// @notice get position of single 1-bit
/// @dev it is assumed that word contains exactly one 1-bit, otherwise the result will be incorrect
/// @param word The word containing only one 1-bit
function getSingleSignificantBit(uint256 word) internal pure returns (uint8 singleBitPos) {
assembly {
singleBitPos := iszero(and(word, 0x5555555555555555555555555555555555555555555555555555555555555555))
singleBitPos := or(singleBitPos, shl(7, iszero(and(word, 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))))
singleBitPos := or(singleBitPos, shl(6, iszero(and(word, 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF))))
singleBitPos := or(singleBitPos, shl(5, iszero(and(word, 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF))))
singleBitPos := or(singleBitPos, shl(4, iszero(and(word, 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF))))
singleBitPos := or(singleBitPos, shl(3, iszero(and(word, 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF))))
singleBitPos := or(singleBitPos, shl(2, iszero(and(word, 0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F))))
singleBitPos := or(singleBitPos, shl(1, iszero(and(word, 0x3333333333333333333333333333333333333333333333333333333333333333))))
}
}
/// @notice get position of most significant 1-bit (leftmost)
/// @dev it is assumed that before the call, a check will be made that the argument (word) is not equal to zero
/// @param word The word containing at least one 1-bit
function getMostSignificantBit(uint256 word) internal pure returns (uint8 mostBitPos) {
assembly {
word := or(word, shr(1, word))
word := or(word, shr(2, word))
word := or(word, shr(4, word))
word := or(word, shr(8, word))
word := or(word, shr(16, word))
word := or(word, shr(32, word))
word := or(word, shr(64, word))
word := or(word, shr(128, word))
word := sub(word, shr(1, word))
}
return (getSingleSignificantBit(word));
}
/// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either
/// to the left (less than or equal to) or right (greater than) of the given tick
/// @param self The mapping in which to compute the next initialized tick
/// @param tick The starting tick
/// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick)
/// @return nextTick The next initialized or uninitialized tick up to 256 ticks away from the current tick
/// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks
function nextTickInTheSameRow(
mapping(int16 => uint256) storage self,
int24 tick,
bool lte
) internal view returns (int24 nextTick, bool initialized) {
{
int24 tickSpacing = Constants.TICK_SPACING;
// compress and round towards negative infinity if negative
assembly {
tick := sub(sdiv(tick, tickSpacing), and(slt(tick, 0), not(iszero(smod(tick, tickSpacing)))))
}
}
if (lte) {
// unpacking not made into a separate function for gas and contract size savings
int16 rowNumber;
uint8 bitNumber;
assembly {
bitNumber := and(tick, 0xFF)
rowNumber := shr(8, tick)
}
uint256 _row = self[rowNumber] << (255 - bitNumber); // all the 1s at or to the right of the current bitNumber
if (_row != 0) {
tick -= int24(255 - getMostSignificantBit(_row));
return (uncompressAndBoundTick(tick), true);
} else {
tick -= int24(bitNumber);
return (uncompressAndBoundTick(tick), false);
}
} else {
// start from the word of the next tick, since the current tick state doesn't matter
tick += 1;
int16 rowNumber;
uint8 bitNumber;
assembly {
bitNumber := and(tick, 0xFF)
rowNumber := shr(8, tick)
}
// all the 1s at or to the left of the bitNumber
uint256 _row = self[rowNumber] >> (bitNumber);
if (_row != 0) {
tick += int24(getSingleSignificantBit(-_row & _row)); // least significant bit
return (uncompressAndBoundTick(tick), true);
} else {
tick += int24(255 - bitNumber);
return (uncompressAndBoundTick(tick), false);
}
}
}
function uncompressAndBoundTick(int24 tick) private pure returns (int24 boundedTick) {
boundedTick = tick * Constants.TICK_SPACING;
if (boundedTick < TickMath.MIN_TICK) {
boundedTick = TickMath.MIN_TICK;
} else if (boundedTick > TickMath.MAX_TICK) {
boundedTick = TickMath.MAX_TICK;
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;
import '../interfaces/IERC20Minimal.sol';
/// @title TransferHelper
/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library TransferHelper {
/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Calls transfer on token contract, errors with TF if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF');
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math library for liquidity
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library LiquidityMath {
/// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows
/// @param x The liquidity before change
/// @param y The delta by which liquidity should be changed
/// @return z The liquidity delta
function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) {
if (y < 0) {
require((z = x - uint128(-y)) < x, 'LS');
} else {
require((z = x + uint128(y)) >= x, 'LA');
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../../AlgebraPool.sol';
// used for testing time dependent behavior
contract SimulationTimeAlgebraPool is AlgebraPool {
// Monday, October 5, 2020 9:00:00 AM GMT-05:00
uint256 public time = 1601906400;
function advanceTime(uint256 by) external {
time += by;
}
function _blockTimestamp() internal view override returns (uint32) {
return uint32(time);
}
function getAverages() external view returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage) {
(TWVolatilityAverage, TWVolumePerLiqAverage) = IDataStorageOperator(dataStorageOperator).getAverages(
_blockTimestamp(),
globalState.fee,
globalState.timepointIndex,
liquidity
);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import '../../interfaces/IAlgebraPoolDeployer.sol';
import './SimulationTimeAlgebraPool.sol';
contract SimulationTimePoolDeployer is IAlgebraPoolDeployer {
struct Parameters {
address dataStorage;
address factory;
address token0;
address token1;
}
/// @inheritdoc IAlgebraPoolDeployer
Parameters public override parameters;
address private factory;
address private owner;
modifier onlyFactory() {
require(msg.sender == factory);
_;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
constructor() {
owner = msg.sender;
}
/// @inheritdoc IAlgebraPoolDeployer
function setFactory(address _factory) external override onlyOwner {
require(_factory != address(0));
require(factory == address(0));
emit Factory(_factory);
factory = _factory;
}
/// @inheritdoc IAlgebraPoolDeployer
function deploy(
address dataStorage,
address _factory,
address token0,
address token1
) external override onlyFactory returns (address pool) {
parameters = Parameters({dataStorage: dataStorage, factory: _factory, token0: token0, token1: token1});
pool = address(new SimulationTimeAlgebraPool{salt: keccak256(abi.encode(token0, token1))}());
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
pragma abicoder v2;
import '../interfaces/IAlgebraPoolDeployer.sol';
import './MockTimeAlgebraPool.sol';
import '../DataStorageOperator.sol';
contract MockTimeAlgebraPoolDeployer {
struct Parameters {
address dataStorage;
address factory;
address token0;
address token1;
}
Parameters public parameters;
event PoolDeployed(address pool);
AdaptiveFee.Configuration baseFeeConfiguration =
AdaptiveFee.Configuration(
3000 - Constants.BASE_FEE, // alpha1
15000 - 3000, // alpha2
360, // beta1
60000, // beta2
59, // gamma1
8500, // gamma2
0, // volumeBeta
10, // volumeGamma
Constants.BASE_FEE // baseFee
);
function deployMock(
address factory,
address token0,
address token1
) external returns (address pool) {
bytes32 initCodeHash = keccak256(type(MockTimeAlgebraPool).creationCode);
DataStorageOperator dataStorage = (new DataStorageOperator(computeAddress(initCodeHash, token0, token1)));
dataStorage.changeFeeConfiguration(baseFeeConfiguration);
parameters = Parameters({dataStorage: address(dataStorage), factory: factory, token0: token0, token1: token1});
pool = address(new MockTimeAlgebraPool{salt: keccak256(abi.encode(token0, token1))}());
emit PoolDeployed(pool);
}
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param token0 first token
/// @param token1 second token
/// @return pool The contract address of the V3 pool
function computeAddress(
bytes32 initCodeHash,
address token0,
address token1
) internal view returns (address pool) {
pool = address(uint256(keccak256(abi.encodePacked(hex'ff', address(this), keccak256(abi.encode(token0, token1)), initCodeHash))));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../AlgebraPool.sol';
// used for testing time dependent behavior
contract MockTimeAlgebraPool is AlgebraPool {
// Monday, October 5, 2020 9:00:00 AM GMT-05:00
uint256 public time = 1601906400;
function setTotalFeeGrowth0Token(uint256 _totalFeeGrowth0Token) external {
totalFeeGrowth0Token = _totalFeeGrowth0Token;
}
function setTotalFeeGrowth1Token(uint256 _totalFeeGrowth1Token) external {
totalFeeGrowth1Token = _totalFeeGrowth1Token;
}
function advanceTime(uint256 by) external {
time += by;
}
function _blockTimestamp() internal view override returns (uint32) {
return uint32(time);
}
function checkBlockTimestamp() external view returns (bool) {
require(super._blockTimestamp() == uint32(block.timestamp));
return true;
}
function getAverages() external view returns (uint112 TWVolatilityAverage, uint256 TWVolumePerLiqAverage) {
(TWVolatilityAverage, TWVolumePerLiqAverage) = IDataStorageOperator(dataStorageOperator).getAverages(
_blockTimestamp(),
globalState.fee,
globalState.timepointIndex,
liquidity
);
}
function getPrevTick() external view returns (int24 tick, int24 currentTick) {
if (globalState.timepointIndex > 2) {
(, uint32 lastTsmp, int56 tickCum, , , , ) = IDataStorageOperator(dataStorageOperator).timepoints(globalState.timepointIndex);
(, uint32 plastTsmp, int56 ptickCum, , , , ) = IDataStorageOperator(dataStorageOperator).timepoints(globalState.timepointIndex - 1);
tick = int24((tickCum - ptickCum) / (lastTsmp - plastTsmp));
}
currentTick = globalState.tick;
}
function getFee() external view returns (uint16 fee) {
return IDataStorageOperator(dataStorageOperator).getFee(_blockTimestamp(), globalState.tick, globalState.timepointIndex, liquidity);
}
function getKeyForPosition(
address owner,
int24 bottomTick,
int24 topTick
) external pure returns (bytes32 key) {
assembly {
key := or(shl(24, or(shl(24, owner), and(bottomTick, 0xFFFFFF))), and(topTick, 0xFFFFFF))
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './interfaces/IAlgebraPoolDeployer.sol';
import './AlgebraPool.sol';
contract AlgebraPoolDeployer is IAlgebraPoolDeployer {
struct Parameters {
address dataStorage;
address factory;
address token0;
address token1;
}
/// @inheritdoc IAlgebraPoolDeployer
Parameters public override parameters;
address private factory;
address private owner;
modifier onlyFactory() {
require(msg.sender == factory);
_;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
constructor() {
owner = msg.sender;
}
/// @inheritdoc IAlgebraPoolDeployer
function setFactory(address _factory) external override onlyOwner {
require(_factory != address(0));
require(factory == address(0));
emit Factory(_factory);
factory = _factory;
}
/// @inheritdoc IAlgebraPoolDeployer
function deploy(
address dataStorage,
address _factory,
address token0,
address token1
) external override onlyFactory returns (address pool) {
parameters = Parameters({dataStorage: dataStorage, factory: _factory, token0: token0, token1: token1});
pool = address(new AlgebraPool{salt: keccak256(abi.encode(token0, token1))}());
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/SafeCast.sol';
import '../libraries/TickMath.sol';
import '../interfaces/IERC20Minimal.sol';
import '../interfaces/callback/IAlgebraSwapCallback.sol';
import '../interfaces/IAlgebraPool.sol';
contract TestAlgebraRouter is IAlgebraSwapCallback {
using SafeCast for uint256;
// flash swaps for an exact amount of token0 in the output pool
function swapForExact0Multi(
address recipient,
address poolInput,
address poolOutput,
uint256 amount0Out
) external {
address[] memory pools = new address[](1);
pools[0] = poolInput;
IAlgebraPool(poolOutput).swap(recipient, false, -amount0Out.toInt256(), TickMath.MAX_SQRT_RATIO - 1, abi.encode(pools, msg.sender));
}
// flash swaps for an exact amount of token1 in the output pool
function swapForExact1Multi(
address recipient,
address poolInput,
address poolOutput,
uint256 amount1Out
) external {
address[] memory pools = new address[](1);
pools[0] = poolInput;
IAlgebraPool(poolOutput).swap(recipient, true, -amount1Out.toInt256(), TickMath.MIN_SQRT_RATIO + 1, abi.encode(pools, msg.sender));
}
event SwapCallback(int256 amount0Delta, int256 amount1Delta);
function algebraSwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) public override {
emit SwapCallback(amount0Delta, amount1Delta);
(address[] memory pools, address payer) = abi.decode(data, (address[], address));
if (pools.length == 1) {
// get the address and amount of the token that we need to pay
address tokenToBePaid = amount0Delta > 0 ? IAlgebraPool(msg.sender).token0() : IAlgebraPool(msg.sender).token1();
int256 amountToBePaid = amount0Delta > 0 ? amount0Delta : amount1Delta;
bool zeroToOne = tokenToBePaid == IAlgebraPool(pools[0]).token1();
IAlgebraPool(pools[0]).swap(
msg.sender,
zeroToOne,
-amountToBePaid,
zeroToOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(new address[](0), payer)
);
} else {
if (amount0Delta > 0) {
IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(payer, msg.sender, uint256(amount0Delta));
} else {
IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(payer, msg.sender, uint256(amount1Delta));
}
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../interfaces/IERC20Minimal.sol';
contract TestERC20 is IERC20Minimal {
mapping(address => uint256) public override balanceOf;
mapping(address => mapping(address => uint256)) public override allowance;
constructor(uint256 amountToMint) {
mint(msg.sender, amountToMint);
}
function mint(address to, uint256 amount) public {
uint256 balanceNext = balanceOf[to] + amount;
require(balanceNext >= amount, 'overflow balance');
balanceOf[to] = balanceNext;
}
function transfer(address recipient, uint256 amount) external override returns (bool) {
uint256 balanceBefore = balanceOf[msg.sender];
require(balanceBefore >= amount, 'insufficient balance');
balanceOf[msg.sender] = balanceBefore - amount;
uint256 balanceRecipient = balanceOf[recipient];
require(balanceRecipient + amount >= balanceRecipient, 'recipient balance overflow');
if (!isDeflationary) {
balanceOf[recipient] = balanceRecipient + amount;
} else {
balanceOf[recipient] = balanceRecipient + (amount - (amount * 5) / 100);
}
emit Transfer(msg.sender, recipient, amount);
return true;
}
function approve(address spender, uint256 amount) external override returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
bool isDeflationary = false;
function setDefl() external {
isDeflationary = true;
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) external override returns (bool) {
uint256 allowanceBefore = allowance[sender][msg.sender];
require(allowanceBefore >= amount, 'allowance insufficient');
allowance[sender][msg.sender] = allowanceBefore - amount;
uint256 balanceRecipient = balanceOf[recipient];
require(balanceRecipient + amount >= balanceRecipient, 'overflow balance recipient');
if (!isDeflationary) {
balanceOf[recipient] = balanceRecipient + amount;
} else {
balanceOf[recipient] = balanceRecipient + (amount - (amount * 5) / 100);
}
uint256 balanceSender = balanceOf[sender];
require(balanceSender >= amount, 'underflow balance sender');
balanceOf[sender] = balanceSender - amount;
emit Transfer(sender, recipient, amount);
return true;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../interfaces/IERC20Minimal.sol';
import '../interfaces/callback/IAlgebraSwapCallback.sol';
import '../interfaces/IAlgebraPool.sol';
contract AlgebraPoolSwapTest is IAlgebraSwapCallback {
int256 private _amount0Delta;
int256 private _amount1Delta;
function getSwapResult(
address pool,
bool zeroToOne,
int256 amountSpecified,
uint160 limitSqrtPrice
)
external
returns (
int256 amount0Delta,
int256 amount1Delta,
uint160 nextSqrtRatio
)
{
(amount0Delta, amount1Delta) = IAlgebraPool(pool).swap(address(0), zeroToOne, amountSpecified, limitSqrtPrice, abi.encode(msg.sender));
(nextSqrtRatio, , , , , , ) = IAlgebraPool(pool).globalState();
}
function algebraSwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external override {
address sender = abi.decode(data, (address));
if (amount0Delta > 0) {
IERC20Minimal(IAlgebraPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta));
} else if (amount1Delta > 0) {
IERC20Minimal(IAlgebraPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta));
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TickMath.sol';
contract TickMathTest {
function getSqrtRatioAtTick(int24 tick) external pure returns (uint160) {
return TickMath.getSqrtRatioAtTick(tick);
}
function getGasCostOfGetSqrtRatioAtTick(int24 tick) external view returns (uint256) {
uint256 gasBefore = gasleft();
TickMath.getSqrtRatioAtTick(tick);
return gasBefore - gasleft();
}
function getTickAtSqrtRatio(uint160 price) external pure returns (int24) {
return TickMath.getTickAtSqrtRatio(price);
}
function getGasCostOfGetTickAtSqrtRatio(uint160 price) external view returns (uint256) {
uint256 gasBefore = gasleft();
TickMath.getTickAtSqrtRatio(price);
return gasBefore - gasleft();
}
function MIN_SQRT_RATIO() external pure returns (uint160) {
return TickMath.MIN_SQRT_RATIO;
}
function MAX_SQRT_RATIO() external pure returns (uint160) {
return TickMath.MAX_SQRT_RATIO;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TickMath.sol';
contract TickMathEchidnaTest {
// uniqueness and increasing order
function checkGetSqrtRatioAtTickInvariants(int24 tick) external pure {
uint160 ratio = TickMath.getSqrtRatioAtTick(tick);
assert(TickMath.getSqrtRatioAtTick(tick - 1) < ratio && ratio < TickMath.getSqrtRatioAtTick(tick + 1));
assert(ratio >= TickMath.MIN_SQRT_RATIO);
assert(ratio <= TickMath.MAX_SQRT_RATIO);
}
// the ratio is always between the returned tick and the returned tick+1
function checkGetTickAtSqrtRatioInvariants(uint160 ratio) external pure {
int24 tick = TickMath.getTickAtSqrtRatio(ratio);
assert(ratio >= TickMath.getSqrtRatioAtTick(tick) && ratio < TickMath.getSqrtRatioAtTick(tick + 1));
assert(tick >= TickMath.MIN_TICK);
assert(tick < TickMath.MAX_TICK);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TickMath.sol';
import '../interfaces/callback/IAlgebraSwapCallback.sol';
import '../interfaces/IAlgebraPool.sol';
contract TestAlgebraReentrantCallee is IAlgebraSwapCallback {
string private constant expectedReason = 'LOK';
function swapToReenter(address pool) external {
IAlgebraPool(pool).swap(address(0), false, 1, TickMath.MAX_SQRT_RATIO - 1, new bytes(0));
}
function algebraSwapCallback(
int256,
int256,
bytes calldata
) external override {
// try to reenter swap
try IAlgebraPool(msg.sender).swap(address(0), false, 1, 0, new bytes(0)) {} catch Error(string memory reason) {
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
}
// try to reenter swap supporting fee
try IAlgebraPool(msg.sender).swapSupportingFeeOnInputTokens(address(0), address(0), false, 1, 0, new bytes(0)) {} catch Error(
string memory reason
) {
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
}
// try to reenter mint
try IAlgebraPool(msg.sender).mint(address(0), address(0), 0, 0, 0, new bytes(0)) {} catch Error(string memory reason) {
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
}
// try to reenter collect
try IAlgebraPool(msg.sender).collect(address(0), 0, 0, 0, 0) {} catch Error(string memory reason) {
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
}
// try to reenter burn
try IAlgebraPool(msg.sender).burn(0, 0, 0) {} catch Error(string memory reason) {
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
}
// try to reenter flash
try IAlgebraPool(msg.sender).flash(address(0), 0, 0, new bytes(0)) {} catch Error(string memory reason) {
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
}
require(false, 'Unable to reenter');
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TickTable.sol';
contract TickTableTest {
using TickTable for mapping(int16 => uint256);
mapping(int16 => uint256) public bitmap;
function toggleTick(int24 tick) external {
bitmap.toggleTick(tick);
}
function getGasCostOfFlipTick(int24 tick) external returns (uint256) {
uint256 gasBefore = gasleft();
bitmap.toggleTick(tick);
return gasBefore - gasleft();
}
function nextTickInTheSameRow(int24 tick, bool lte) external view returns (int24 next, bool initialized) {
return bitmap.nextTickInTheSameRow(tick, lte);
}
function getGasCostOfNextTickInTheSameRow(int24 tick, bool lte) external view returns (uint256) {
uint256 gasBefore = gasleft();
bitmap.nextTickInTheSameRow(tick, lte);
return gasBefore - gasleft();
}
// returns whether the given tick is initialized
function isInitialized(int24 tick) external view returns (bool) {
(int24 next, bool initialized) = bitmap.nextTickInTheSameRow(tick, true);
return next == tick ? initialized : false;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TickTable.sol';
contract TickTableEchidnaTest {
using TickTable for mapping(int16 => uint256);
mapping(int16 => uint256) private bitmap;
// returns whether the given tick is initialized
function isInitialized(int24 tick) private view returns (bool) {
(int24 next, bool initialized) = bitmap.nextTickInTheSameRow(tick, true);
return next == tick ? initialized : false;
}
function toggleTick(int24 tick) external {
tick = (tick / 60);
tick = tick * 60;
require(tick >= -887272);
require(tick <= 887272);
require(tick % 60 == 0);
bool before = isInitialized(tick);
bitmap.toggleTick(tick);
assert(isInitialized(tick) == !before);
}
function checkNextInitializedTickWithinOneWordInvariants(int24 tick, bool lte) external view {
tick = (tick / 60);
tick = tick * 60;
require(tick % 60 == 0);
require(tick >= -887272);
require(tick <= 887272);
(int24 next, bool initialized) = bitmap.nextTickInTheSameRow(tick, lte);
if (lte) {
// type(int24).min + 256
assert(next <= tick);
assert(tick - next < 256 * 60);
// all the ticks between the input tick and the next tick should be uninitialized
for (int24 i = tick; i > next; i -= 60) {
assert(!isInitialized(i));
}
assert(isInitialized(next) == initialized);
} else {
// type(int24).max - 256
require(tick < 887272);
assert(next > tick);
assert(next - tick <= 256 * 60);
// all the ticks between the input tick and the next tick should be uninitialized
for (int24 i = tick + 60; i < next; i += 60) {
assert(!isInitialized(i));
}
assert(isInitialized(next) == initialized);
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TickTable.sol';
contract BitMathTest {
function mostSignificantBit(uint256 x) external pure returns (uint8 r) {
return TickTable.getMostSignificantBit(x);
}
function getGasCostOfMostSignificantBit(uint256 x) external view returns (uint256) {
uint256 gasBefore = gasleft();
TickTable.getMostSignificantBit(x);
return gasBefore - gasleft();
}
function leastSignificantBit(uint256 x) external pure returns (uint8 r) {
return TickTable.getSingleSignificantBit(-x & x);
}
function getGasCostOfLeastSignificantBit(uint256 x) external view returns (uint256) {
uint256 gasBefore = gasleft();
TickTable.getSingleSignificantBit(-x & x);
return gasBefore - gasleft();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TickTable.sol';
contract BitMathEchidnaTest {
function mostSignificantBitInvariant(uint256 input) external pure {
require(input > 0);
uint8 msb = TickTable.getMostSignificantBit(input);
assert(input >= (uint256(2)**msb));
assert(msb == 255 || input < uint256(2)**(msb + 1));
}
function leastSignificantBitInvariant(uint256 input) external pure {
require(input > 0);
uint8 lsb = TickTable.getSingleSignificantBit(-input & input);
assert(input & (uint256(2)**lsb) != 0);
assert(input & (uint256(2)**lsb - 1) == 0);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/LowGasSafeMath.sol';
import '../libraries/SafeCast.sol';
contract SafeMathTest {
function add(uint256 x, uint256 y) external pure returns (uint256 z) {
return LowGasSafeMath.add(x, y);
}
function sub(uint256 x, uint256 y) external pure returns (uint256 z) {
return LowGasSafeMath.sub(x, y);
}
function mul(uint256 x, uint256 y) external pure returns (uint256 z) {
return LowGasSafeMath.mul(x, y);
}
function addInt(int256 x, int256 y) external pure returns (int256 z) {
return LowGasSafeMath.add(x, y);
}
function subInt(int256 x, int256 y) external pure returns (int256 z) {
return LowGasSafeMath.sub(x, y);
}
function add128(uint128 x, uint128 y) external pure returns (uint128 z) {
return LowGasSafeMath.add128(x, y);
}
function toUint160(uint256 y) external pure returns (uint160 z) {
return SafeCast.toUint160(y);
}
function toInt128(int256 y) external pure returns (int128 z) {
return SafeCast.toInt128(y);
}
function toInt256(uint256 y) external pure returns (int256 z) {
return SafeCast.toInt256(y);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/LowGasSafeMath.sol';
contract LowGasSafeMathEchidnaTest {
function checkAdd(uint256 x, uint256 y) external pure {
uint256 z = LowGasSafeMath.add(x, y);
assert(z == x + y);
assert(z >= x && z >= y);
}
function checkSub(uint256 x, uint256 y) external pure {
uint256 z = LowGasSafeMath.sub(x, y);
assert(z == x - y);
assert(z <= x);
}
function checkMul(uint256 x, uint256 y) external pure {
uint256 z = LowGasSafeMath.mul(x, y);
assert(z == x * y);
assert(x == 0 || y == 0 || (z >= x && z >= y));
}
function checkAddi(int256 x, int256 y) external pure {
int256 z = LowGasSafeMath.add(x, y);
assert(z == x + y);
assert(y < 0 ? z < x : z >= x);
}
function checkSubi(int256 x, int256 y) external pure {
int256 z = LowGasSafeMath.sub(x, y);
assert(z == x - y);
assert(y < 0 ? z > x : z <= x);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
pragma abicoder v2;
import '../libraries/TickManager.sol';
contract TickTest {
using TickManager for mapping(int24 => TickManager.Tick);
mapping(int24 => TickManager.Tick) public ticks;
function setTick(int24 tick, TickManager.Tick memory data) external {
ticks[tick] = data;
}
function getInnerFeeGrowth(
int24 bottomTick,
int24 topTick,
int24 currentTick,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token
) external view returns (uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token) {
return ticks.getInnerFeeGrowth(bottomTick, topTick, currentTick, totalFeeGrowth0Token, totalFeeGrowth1Token);
}
function update(
int24 tick,
int24 currentTick,
int128 liquidityDelta,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token,
uint160 secondsPerLiquidityCumulative,
int56 tickCumulative,
uint32 time,
bool upper
) external returns (bool flipped) {
return
ticks.update(
tick,
currentTick,
liquidityDelta,
totalFeeGrowth0Token,
totalFeeGrowth1Token,
secondsPerLiquidityCumulative,
tickCumulative,
time,
upper
);
}
function clear(int24 tick) external {
delete ticks[tick];
}
function cross(
int24 tick,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token,
uint160 secondsPerLiquidityCumulative,
int56 tickCumulative,
uint32 time
) external returns (int128 liquidityDelta) {
return ticks.cross(tick, totalFeeGrowth0Token, totalFeeGrowth1Token, secondsPerLiquidityCumulative, tickCumulative, time);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/TickManager.sol';
contract TickOverflowSafetyEchidnaTest {
using TickManager for mapping(int24 => TickManager.Tick);
int24 private constant MIN_TICK = -16;
int24 private constant MAX_TICK = 16;
uint128 private constant MAX_LIQUIDITY = type(uint128).max / 32;
mapping(int24 => TickManager.Tick) private ticks;
int24 private tick = 0;
// used to track how much total liquidity has been added. should never be negative
int256 totalLiquidity = 0;
// half the cap of fee growth has happened, this can overflow
uint256 private totalFeeGrowth0Token = type(uint256).max / 2;
uint256 private totalFeeGrowth1Token = type(uint256).max / 2;
// how much total growth has happened, this cannot overflow
uint256 private totalGrowth0 = 0;
uint256 private totalGrowth1 = 0;
function increaseTotalFeeGrowth0Token(uint256 amount) external {
require(totalGrowth0 + amount > totalGrowth0); // overflow check
totalFeeGrowth0Token += amount; // overflow desired
totalGrowth0 += amount;
}
function increaseTotalFeeGrowth1Token(uint256 amount) external {
require(totalGrowth1 + amount > totalGrowth1); // overflow check
totalFeeGrowth1Token += amount; // overflow desired
totalGrowth1 += amount;
}
function setPosition(
int24 bottomTick,
int24 topTick,
int128 liquidityDelta
) external {
require(bottomTick > MIN_TICK);
require(topTick < MAX_TICK);
require(bottomTick < topTick);
bool flippedLower = ticks.update(
bottomTick,
tick,
liquidityDelta,
totalFeeGrowth0Token,
totalFeeGrowth1Token,
0,
0,
uint32(block.timestamp),
false
);
bool flippedUpper = ticks.update(topTick, tick, liquidityDelta, totalFeeGrowth0Token, totalFeeGrowth1Token, 0, 0, uint32(block.timestamp), true);
if (flippedLower) {
if (liquidityDelta < 0) {
assert(ticks[bottomTick].liquidityTotal == 0);
delete ticks[bottomTick];
} else assert(ticks[bottomTick].liquidityTotal > 0);
}
if (flippedUpper) {
if (liquidityDelta < 0) {
assert(ticks[topTick].liquidityTotal == 0);
delete ticks[topTick];
} else assert(ticks[topTick].liquidityTotal > 0);
}
totalLiquidity += liquidityDelta;
// requires should have prevented this
assert(totalLiquidity >= 0);
if (totalLiquidity == 0) {
totalGrowth0 = 0;
totalGrowth1 = 0;
}
}
function moveToTick(int24 target) external {
require(target > MIN_TICK);
require(target < MAX_TICK);
while (tick != target) {
if (tick < target) {
if (ticks[tick + 1].liquidityTotal > 0) ticks.cross(tick + 1, totalFeeGrowth0Token, totalFeeGrowth1Token, 0, 0, uint32(block.timestamp));
tick++;
} else {
if (ticks[tick].liquidityTotal > 0) ticks.cross(tick, totalFeeGrowth0Token, totalFeeGrowth1Token, 0, 0, uint32(block.timestamp));
tick--;
}
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/LiquidityMath.sol';
contract LiquidityMathTest {
function addDelta(uint128 x, int128 y) external pure returns (uint128 z) {
return LiquidityMath.addDelta(x, y);
}
function getGasCostOfAddDelta(uint128 x, int128 y) external view returns (uint256) {
uint256 gasBefore = gasleft();
LiquidityMath.addDelta(x, y);
return gasBefore - gasleft();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../interfaces/IAlgebraVirtualPool.sol';
contract MockTimeVirtualPool is IAlgebraVirtualPool {
uint32 public timestamp;
bool private isExist = true;
bool private isStarted = true;
int24 public currentTick;
function setIsExist(bool _isExist) external {
isExist = _isExist;
}
function setIsStarted(bool _isStarted) external {
isStarted = _isStarted;
}
function increaseCumulative(uint32 currentTimestamp) external override returns (Status) {
if (!isExist) return Status.NOT_EXIST;
if (!isStarted) return Status.NOT_STARTED;
timestamp = currentTimestamp;
return Status.ACTIVE;
}
function cross(int24 nextTick, bool zeroToOne) external override {
zeroToOne;
require(isExist, 'Virtual pool not exist');
currentTick = nextTick;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/FullMath.sol';
contract FullMathTest {
function mulDiv(
uint256 x,
uint256 y,
uint256 z
) external pure returns (uint256) {
return FullMath.mulDiv(x, y, z);
}
function mulDivRoundingUp(
uint256 x,
uint256 y,
uint256 z
) external pure returns (uint256) {
return FullMath.mulDivRoundingUp(x, y, z);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/FullMath.sol';
contract FullMathEchidnaTest {
function checkMulDivRounding(
uint256 x,
uint256 y,
uint256 d
) external pure {
require(d > 0);
uint256 ceiled = FullMath.mulDivRoundingUp(x, y, d);
uint256 floored = FullMath.mulDiv(x, y, d);
if (mulmod(x, y, d) > 0) {
assert(ceiled - floored == 1);
} else {
assert(ceiled == floored);
}
}
function checkMulDiv(
uint256 x,
uint256 y,
uint256 d
) external pure {
require(d > 0);
uint256 z = FullMath.mulDiv(x, y, d);
if (x == 0 || y == 0) {
assert(z == 0);
return;
}
// recompute x and y via mulDiv of the result of floor(x*y/d), should always be less than original inputs by < d
uint256 x2 = FullMath.mulDiv(z, d, y);
uint256 y2 = FullMath.mulDiv(z, d, x);
assert(x2 <= x);
assert(y2 <= y);
assert(x - x2 < d);
assert(y - y2 < d);
}
function checkMulDivRoundingUp(
uint256 x,
uint256 y,
uint256 d
) external pure {
require(d > 0);
uint256 z = FullMath.mulDivRoundingUp(x, y, d);
if (x == 0 || y == 0) {
assert(z == 0);
return;
}
// recompute x and y via mulDiv of the result of floor(x*y/d), should always be less than original inputs by < d
uint256 x2 = FullMath.mulDiv(z, d, y);
uint256 y2 = FullMath.mulDiv(z, d, x);
assert(x2 >= x);
assert(y2 >= y);
assert(x2 - x < d);
assert(y2 - y < d);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
pragma abicoder v2;
import '../libraries/DataStorage.sol';
contract DataStorageTest {
uint256 private constant UINT16_MODULO = 65536;
using DataStorage for DataStorage.Timepoint[UINT16_MODULO];
DataStorage.Timepoint[UINT16_MODULO] public timepoints;
uint32 public time;
int24 public tick;
uint128 public liquidity;
uint16 public index;
struct InitializeParams {
uint32 time;
int24 tick;
uint128 liquidity;
}
function initialize(InitializeParams calldata params) external {
time = params.time;
tick = params.tick;
liquidity = params.liquidity;
timepoints.initialize(params.time, tick);
}
function advanceTime(uint32 by) public {
time += by;
}
struct UpdateParams {
uint32 advanceTimeBy;
int24 tick;
uint128 liquidity;
}
// write an timepoint, then change tick and liquidity
function update(UpdateParams calldata params) external {
advanceTime(params.advanceTimeBy);
index = timepoints.write(index, time, tick, liquidity, 0); //TODO: fix for testing
tick = params.tick;
liquidity = params.liquidity;
}
function batchUpdate(UpdateParams[] calldata params) external {
// sload everything
int24 _tick = tick;
uint128 _liquidity = liquidity;
uint16 _index = index;
uint32 _time = time;
for (uint256 i = 0; i < params.length; i++) {
_time += params[i].advanceTimeBy;
_index = timepoints.write(_index, _time, _tick, _liquidity, 0);
_tick = params[i].tick;
_liquidity = params[i].liquidity;
}
// sstore everything
tick = _tick;
liquidity = _liquidity;
index = _index;
time = _time;
}
function getTimepoints(uint32[] calldata secondsAgos)
external
view
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
)
{
return timepoints.getTimepoints(time, secondsAgos, tick, index, liquidity);
}
function getGasCostOfGetPoints(uint32[] calldata secondsAgos) external view returns (uint256) {
(uint32 _time, int24 _tick, uint128 _liquidity, uint16 _index) = (time, tick, liquidity, index);
uint256 gasBefore = gasleft();
timepoints.getTimepoints(_time, secondsAgos, _tick, _index, _liquidity);
return gasBefore - gasleft();
}
function volatilityOnRange(
uint32 dt,
int24 tick0,
int24 tick1,
int24 avgTick0,
int24 avgTick1
) external pure returns (uint256) {
return DataStorage._volatilityOnRange(dt, tick0, tick1, avgTick0, avgTick1);
}
function getAverageTick() external view returns (int256) {
uint32 lastTimestamp = timepoints[index].blockTimestamp;
int56 lastTickCumulative = timepoints[index].tickCumulative;
uint16 oldestIndex;
if (timepoints[index + 1].initialized) {
oldestIndex = index + 1;
}
(uint32 _time, int24 _tick, uint16 _index) = (time, tick, index);
return timepoints._getAverageTick(_time, _tick, _index, oldestIndex, lastTimestamp, lastTickCumulative);
}
function window() external pure returns (uint256) {
return DataStorage.WINDOW;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
pragma abicoder v2;
import './DataStorageTest.sol';
contract DataStorageEchidnaTest {
DataStorageTest private dataStorage;
bool private initialized;
uint32 private timePassed;
constructor() {
dataStorage = new DataStorageTest();
}
function initialize(
uint32 time,
int24 tick,
uint128 liquidity
) external {
require(tick % 60 == 0);
dataStorage.initialize(DataStorageTest.InitializeParams({time: time, tick: tick, liquidity: liquidity}));
initialized = true;
}
function limitTimePassed(uint32 by) private {
require(timePassed + by >= timePassed);
timePassed += by;
}
function advanceTime(uint32 by) public {
limitTimePassed(by);
dataStorage.advanceTime(by);
}
// write an timepoint, then change tick and liquidity
function update(
uint32 advanceTimeBy,
int24 tick,
uint128 liquidity
) external {
require(initialized);
limitTimePassed(advanceTimeBy);
dataStorage.update(DataStorageTest.UpdateParams({advanceTimeBy: advanceTimeBy, tick: tick, liquidity: liquidity}));
}
function checkTimeWeightedResultAssertions(uint32 secondsAgo0, uint32 secondsAgo1) private view {
require(secondsAgo0 != secondsAgo1);
require(initialized);
// secondsAgo0 should be the larger one
if (secondsAgo0 < secondsAgo1) (secondsAgo0, secondsAgo1) = (secondsAgo1, secondsAgo0);
uint32 timeElapsed = secondsAgo0 - secondsAgo1;
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = secondsAgo0;
secondsAgos[1] = secondsAgo1;
(int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulatives, , ) = dataStorage.getTimepoints(secondsAgos);
int56 timeWeightedTick = (tickCumulatives[1] - tickCumulatives[0]) / timeElapsed;
uint256 timeWeightedHarmonicMeanLiquidity = (uint256(timeElapsed) * type(uint160).max) /
(uint256(secondsPerLiquidityCumulatives[1] - secondsPerLiquidityCumulatives[0]) << 32);
assert(timeWeightedHarmonicMeanLiquidity <= type(uint128).max);
assert(timeWeightedTick <= type(int24).max);
assert(timeWeightedTick >= type(int24).min);
}
function echidna_indexAlwaysLtCardinality() external view returns (bool) {
return dataStorage.index() < 65536 || !initialized;
}
function echidna_avgTickNotOverflows() external view returns (bool) {
int256 res = dataStorage.getAverageTick();
return (res <= type(int24).max && res >= type(int24).min);
}
function echidna_canAlwaysGetPoints0IfInitialized() external view returns (bool) {
if (!initialized) {
return true;
}
uint32[] memory arr = new uint32[](1);
arr[0] = 0;
(bool success, ) = address(dataStorage).staticcall(abi.encodeWithSelector(DataStorageTest.getTimepoints.selector, arr));
return success;
}
function checkVolatilityOnRangeNotOverflowUint88(
uint32 dt,
int24 tick0,
int24 tick1,
int24 avgTick0,
int24 avgTick1
) external view {
uint256 res = dataStorage.volatilityOnRange(dt, tick0, tick1, avgTick0, avgTick1);
assert(res <= type(uint88).max);
}
function checkTwoAdjacentTimepointsTickCumulativeModTimeElapsedAlways0(uint16 index) external view {
// check that the timepoints are initialized, and that the index is not the oldest timepoint
require(index < 65536 && index != (dataStorage.index() + 1) % 65536);
(bool initialized0, uint32 blockTimestamp0, int56 tickCumulative0, , , , ) = dataStorage.timepoints(index == 0 ? 65536 - 1 : index - 1);
(bool initialized1, uint32 blockTimestamp1, int56 tickCumulative1, , , , ) = dataStorage.timepoints(index);
require(initialized0);
require(initialized1);
uint32 timeElapsed = blockTimestamp1 - blockTimestamp0;
assert(timeElapsed > 0);
assert((tickCumulative1 - tickCumulative0) % timeElapsed == 0);
}
function checkTimeWeightedAveragesAlwaysFitsType(uint32 secondsAgo) external view {
require(initialized);
require(secondsAgo > 0);
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = secondsAgo;
secondsAgos[1] = 0;
(
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives, //TODO: volumePerLiq
,
) = dataStorage.getTimepoints(secondsAgos);
// compute the time weighted tick, rounded towards negative infinity
int56 numerator = tickCumulatives[1] - tickCumulatives[0];
int56 timeWeightedTick = numerator / int56(secondsAgo);
if (numerator < 0 && numerator % int56(secondsAgo) != 0) {
timeWeightedTick--;
}
// the time weighted averages fit in their respective accumulated types
assert(timeWeightedTick <= type(int24).max && timeWeightedTick >= type(int24).min);
uint256 timeWeightedHarmonicMeanLiquidity = (uint256(secondsAgo) * type(uint160).max) /
(uint256(secondsPerLiquidityCumulatives[1] - secondsPerLiquidityCumulatives[0]) << 32);
assert(timeWeightedHarmonicMeanLiquidity <= type(uint128).max);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/PriceMovementMath.sol';
contract PriceMovementMathTest {
function movePriceTowardsTarget(
uint160 sqrtP,
uint160 sqrtPTarget,
uint128 liquidity,
int256 amountRemaining,
uint16 feePips
)
external
pure
returns (
uint160 sqrtQ,
uint256 amountIn,
uint256 amountOut,
uint256 feeAmount
)
{
return PriceMovementMath.movePriceTowardsTarget(sqrtPTarget < sqrtP, sqrtP, sqrtPTarget, liquidity, amountRemaining, feePips);
}
function getGasCostOfmovePriceTowardsTarget(
uint160 sqrtP,
uint160 sqrtPTarget,
uint128 liquidity,
int256 amountRemaining,
uint16 feePips
) external view returns (uint256) {
uint256 gasBefore = gasleft();
PriceMovementMath.movePriceTowardsTarget(sqrtPTarget < sqrtP, sqrtP, sqrtPTarget, liquidity, amountRemaining, feePips);
return gasBefore - gasleft();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/PriceMovementMath.sol';
contract PriceMovementMathEchidnaTest {
function checkmovePriceTowardsTargetInvariants(
uint160 sqrtPriceRaw,
uint160 sqrtPriceTargetRaw,
uint128 liquidity,
int256 amountRemaining,
uint16 feePips
) external pure {
require(sqrtPriceRaw > 0);
require(sqrtPriceTargetRaw > 0);
require(feePips > 0);
require(feePips < 1e6);
(uint160 sqrtQ, uint256 amountIn, uint256 amountOut, uint256 feeAmount) = PriceMovementMath.movePriceTowardsTarget(
sqrtPriceTargetRaw <= sqrtPriceRaw,
sqrtPriceRaw,
sqrtPriceTargetRaw,
liquidity,
amountRemaining,
feePips
);
assert(amountIn <= type(uint256).max - feeAmount);
if (amountRemaining < 0) {
assert(amountOut <= uint256(-amountRemaining));
} else {
assert(amountIn + feeAmount <= uint256(amountRemaining));
}
if (sqrtPriceRaw == sqrtPriceTargetRaw) {
assert(amountIn == 0);
assert(amountOut == 0);
assert(feeAmount == 0);
assert(sqrtQ == sqrtPriceTargetRaw);
}
// didn't reach price target, entire amount must be consumed
if (sqrtQ != sqrtPriceTargetRaw) {
if (amountRemaining < 0) assert(amountOut == uint256(-amountRemaining));
else assert(amountIn + feeAmount == uint256(amountRemaining));
}
// next price is between price and price target
if (sqrtPriceTargetRaw <= sqrtPriceRaw) {
assert(sqrtQ <= sqrtPriceRaw);
assert(sqrtQ >= sqrtPriceTargetRaw);
} else {
assert(sqrtQ >= sqrtPriceRaw);
assert(sqrtQ <= sqrtPriceTargetRaw);
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import '../libraries/AdaptiveFee.sol';
contract AdaptiveFeeEchidnaTest {
function expInvariants(uint256 x, uint16 gamma) external pure {
require(gamma != 0);
if (x >= 6 * gamma) return;
uint256 g8 = uint256(gamma)**8;
uint256 exp = AdaptiveFee.exp(x, gamma, g8);
assert(exp < 2**137);
}
function sigmoidInvariants(
uint256 x,
uint16 gamma,
uint16 alpha,
uint256 beta
) external pure {
require(gamma != 0);
uint256 res = AdaptiveFee.sigmoid(x, gamma, alpha, beta);
assert(res <= type(uint16).max);
assert(res <= alpha);
}
function getFeeInvariants(
uint88 volatility,
uint256 volumePerLiquidity,
uint16 gamma1,
uint16 gamma2,
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 volumeGamma,
uint32 volumeBeta,
uint16 baseFee
) external pure returns (uint256 fee) {
require(uint256(alpha1) + uint256(alpha2) + uint256(baseFee) <= type(uint16).max, 'Max fee exceeded');
require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0');
uint256 sigm1 = AdaptiveFee.sigmoid(volatility, gamma1, alpha1, beta1);
uint256 sigm2 = AdaptiveFee.sigmoid(volatility, gamma2, alpha2, beta2);
assert(sigm1 + sigm2 <= type(uint16).max);
fee = baseFee + AdaptiveFee.sigmoid(volumePerLiquidity, volumeGamma, uint16(sigm1 + sigm2), volumeBeta);
assert(fee <= type(uint16).max);
}
}{
"optimizer": {
"enabled": true,
"runs": 0
},
"metadata": {
"bytecodeHash": "none",
"useLiteralContent": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"int24","name":"bottomTick","type":"int24"},{"indexed":true,"internalType":"int24","name":"topTick","type":"int24"},{"indexed":false,"internalType":"uint128","name":"liquidityAmount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"int24","name":"bottomTick","type":"int24"},{"indexed":true,"internalType":"int24","name":"topTick","type":"int24"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"Collect","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"communityFee0New","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"communityFee1New","type":"uint8"}],"name":"CommunityFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"fee","type":"uint16"}],"name":"Fee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid1","type":"uint256"}],"name":"Flash","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"virtualPoolAddress","type":"address"}],"name":"Incentive","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint160","name":"price","type":"uint160"},{"indexed":false,"internalType":"int24","name":"tick","type":"int24"}],"name":"Initialize","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"liquidityCooldown","type":"uint32"}],"name":"LiquidityCooldown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"int24","name":"bottomTick","type":"int24"},{"indexed":true,"internalType":"int24","name":"topTick","type":"int24"},{"indexed":false,"internalType":"uint128","name":"liquidityAmount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"int256","name":"amount0","type":"int256"},{"indexed":false,"internalType":"int256","name":"amount1","type":"int256"},{"indexed":false,"internalType":"uint160","name":"price","type":"uint160"},{"indexed":false,"internalType":"uint128","name":"liquidity","type":"uint128"},{"indexed":false,"internalType":"int24","name":"tick","type":"int24"}],"name":"Swap","type":"event"},{"inputs":[],"name":"activeIncentive","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"bottomTick","type":"int24"},{"internalType":"int24","name":"topTick","type":"int24"},{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"int24","name":"bottomTick","type":"int24"},{"internalType":"int24","name":"topTick","type":"int24"},{"internalType":"uint128","name":"amount0Requested","type":"uint128"},{"internalType":"uint128","name":"amount1Requested","type":"uint128"}],"name":"collect","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dataStorageOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int24","name":"bottomTick","type":"int24"},{"internalType":"int24","name":"topTick","type":"int24"}],"name":"getInnerCumulatives","outputs":[{"internalType":"int56","name":"innerTickCumulative","type":"int56"},{"internalType":"uint160","name":"innerSecondsSpentPerLiquidity","type":"uint160"},{"internalType":"uint32","name":"innerSecondsSpent","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"secondsAgos","type":"uint32[]"}],"name":"getTimepoints","outputs":[{"internalType":"int56[]","name":"tickCumulatives","type":"int56[]"},{"internalType":"uint160[]","name":"secondsPerLiquidityCumulatives","type":"uint160[]"},{"internalType":"uint112[]","name":"volatilityCumulatives","type":"uint112[]"},{"internalType":"uint256[]","name":"volumePerAvgLiquiditys","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalState","outputs":[{"internalType":"uint160","name":"price","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint16","name":"fee","type":"uint16"},{"internalType":"uint16","name":"timepointIndex","type":"uint16"},{"internalType":"uint8","name":"communityFeeToken0","type":"uint8"},{"internalType":"uint8","name":"communityFeeToken1","type":"uint8"},{"internalType":"bool","name":"unlocked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint160","name":"initialPrice","type":"uint160"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidity","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityCooldown","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLiquidityPerTick","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"int24","name":"bottomTick","type":"int24"},{"internalType":"int24","name":"topTick","type":"int24"},{"internalType":"uint128","name":"liquidityDesired","type":"uint128"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mint","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint128","name":"liquidityActual","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"positions","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint32","name":"lastLiquidityAddTimestamp","type":"uint32"},{"internalType":"uint256","name":"innerFeeGrowth0Token","type":"uint256"},{"internalType":"uint256","name":"innerFeeGrowth1Token","type":"uint256"},{"internalType":"uint128","name":"fees0","type":"uint128"},{"internalType":"uint128","name":"fees1","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"communityFee0","type":"uint8"},{"internalType":"uint8","name":"communityFee1","type":"uint8"}],"name":"setCommunityFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"virtualPoolAddress","type":"address"}],"name":"setIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newLiquidityCooldown","type":"uint32"}],"name":"setLiquidityCooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"zeroToOne","type":"bool"},{"internalType":"int256","name":"amountRequired","type":"int256"},{"internalType":"uint160","name":"limitSqrtPrice","type":"uint160"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"int256","name":"amount0","type":"int256"},{"internalType":"int256","name":"amount1","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"zeroToOne","type":"bool"},{"internalType":"int256","name":"amountRequired","type":"int256"},{"internalType":"uint160","name":"limitSqrtPrice","type":"uint160"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapSupportingFeeOnInputTokens","outputs":[{"internalType":"int256","name":"amount0","type":"int256"},{"internalType":"int256","name":"amount1","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tickSpacing","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int16","name":"","type":"int16"}],"name":"tickTable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"","type":"int24"}],"name":"ticks","outputs":[{"internalType":"uint128","name":"liquidityTotal","type":"uint128"},{"internalType":"int128","name":"liquidityDelta","type":"int128"},{"internalType":"uint256","name":"outerFeeGrowth0Token","type":"uint256"},{"internalType":"uint256","name":"outerFeeGrowth1Token","type":"uint256"},{"internalType":"int56","name":"outerTickCumulative","type":"int56"},{"internalType":"uint160","name":"outerSecondsPerLiquidity","type":"uint160"},{"internalType":"uint32","name":"outerSecondsSpent","type":"uint32"},{"internalType":"bool","name":"initialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"timepoints","outputs":[{"internalType":"bool","name":"initialized","type":"bool"},{"internalType":"uint32","name":"blockTimestamp","type":"uint32"},{"internalType":"int56","name":"tickCumulative","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityCumulative","type":"uint160"},{"internalType":"uint88","name":"volatilityCumulative","type":"uint88"},{"internalType":"int24","name":"averageTick","type":"int24"},{"internalType":"uint144","name":"volumePerLiquidityCumulative","type":"uint144"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFeeGrowth0Token","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFeeGrowth1Token","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6101006040523480156200001257600080fd5b5033806001600160a01b031663890357306040518163ffffffff1660e01b815260040160806040518083038186803b1580156200004e57600080fd5b505afa15801562000063573d6000803e3d6000fd5b505050506040513d60808110156200007a57600080fd5b508051602082015160408301516060938401516001600160601b031990851b811660e05290841b811660c05290831b811660a052911b166080525060028054601960ba1b61ffff60b81b1990911617905560805160601c60a05160601c60c05160601c60e05160601c61550a620001a860003980610a4a5280610b885280610ec15280611150528061136b52806115f252806116a052806116da52806127bf52806128bd52806136b15250806109f15280610aea5280610bae5280610e6f528061108752806113285280611628528061166a528061278a528061355b525080610c6c5280610f8552806118b652806119e452806128825280613720525080610d8052806117875280611d905280612a005280613332528061396852806140dd52806141a9525061550a6000f3fe608060405234801561001057600080fd5b50600436106101545760003560e01c80630dfe168114610159578063128acb081461017d57806317e25b3c1461022a5780631a6865021461024b578063289fe9b01461026f57806329047dfa14610294578063490e6cbc1461029c5780634f1eb3d814610326578063514ea4bf146103a65780636378ae441461040b57806370cf754a14610425578063713346941461042d57806374eceae6146104ca5780637c0112b7146105465780637c1fe0c81461056e578063920c34e5146105945780639d3a5241146105ef578063a34123a714610780578063aafe29c0146107ba578063c45a01551461088c578063c677e3e014610894578063d0c93a7c146108b4578063d21220a7146108d3578063e76c01e4146108db578063ecdecf4214610937578063f30dba931461093f578063f637731d146109c1578063facb0eb1146109e7575b600080fd5b6101616109ef565b604080516001600160a01b039092168252519081900360200190f35b610211600480360360a081101561019357600080fd5b6001600160a01b0382358116926020810135151592604082013592606083013516919081019060a081016080820135600160201b8111156101d357600080fd5b8201836020820111156101e557600080fd5b803590602001918460018302840111600160201b8311171561020657600080fd5b509092509050610a13565b6040805192835260208301919091528051918290030190f35b610232610c4f565b6040805163ffffffff9092168252519081900360200190f35b610253610c5b565b604080516001600160801b039092168252519081900360200190f35b6102926004803603602081101561028557600080fd5b503563ffffffff16610c6a565b005b610161610d7e565b610292600480360360808110156102b257600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b8111156102e857600080fd5b8201836020820111156102fa57600080fd5b803590602001918460018302840111600160201b8311171561031b57600080fd5b509092509050610da2565b610377600480360360a081101561033c57600080fd5b506001600160a01b03813516906020810135600290810b91604081013590910b906001600160801b0360608201358116916080013516611222565b60405180836001600160801b03168152602001826001600160801b031681526020019250505060405180910390f35b6103c3600480360360208110156103bc57600080fd5b503561141c565b604080516001600160801b03978816815263ffffffff90961660208701528581019490945260608501929092528416608084015290921660a082015290519081900360c00190f35b610413611465565b60408051918252519081900360200190f35b61025361146b565b610211600480360360c081101561044357600080fd5b6001600160a01b0382358116926020810135821692604082013515159260608301359260808101359091169181019060c0810160a0820135600160201b81111561048c57600080fd5b82018360208201111561049e57600080fd5b803590602001918460018302840111600160201b831117156104bf57600080fd5b50909250905061147e565b6104e7600480360360208110156104e057600080fd5b503561177a565b60408051971515885263ffffffff909616602088015260069490940b868601526001600160a01b0390921660608601526001600160581b0316608085015260020b60a08401526001600160901b031660c0830152519081900360e00190f35b6102926004803603604081101561055c57600080fd5b5060ff8135811691602001351661184b565b6102926004803603602081101561058457600080fd5b50356001600160a01b03166119e2565b6105be600480360360408110156105aa57600080fd5b508035600290810b9160200135900b611ad1565b6040805160069490940b84526001600160a01b03909216602084015263ffffffff1682820152519081900360600190f35b61065d6004803603602081101561060557600080fd5b810190602081018135600160201b81111561061f57600080fd5b82018360208201111561063157600080fd5b803590602001918460208302840111600160201b8311171561065257600080fd5b509092509050611d88565b6040518080602001806020018060200180602001858103855289818151815260200191508051906020019060200280838360005b838110156106a9578181015183820152602001610691565b50505050905001858103845288818151815260200191508051906020019060200280838360005b838110156106e85781810151838201526020016106d0565b50505050905001858103835287818151815260200191508051906020019060200280838360005b8381101561072757818101518382015260200161070f565b50505050905001858103825286818151815260200191508051906020019060200280838360005b8381101561076657818101518382015260200161074e565b505050509050019850505050505050505060405180910390f35b6102116004803603606081101561079657600080fd5b508035600290810b91602081013590910b90604001356001600160801b03166120ed565b610865600480360360c08110156107d057600080fd5b6001600160a01b0382358116926020810135909116916040820135600290810b92606081013590910b916001600160801b03608083013516919081019060c0810160a0820135600160201b81111561082757600080fd5b82018360208201111561083957600080fd5b803590602001918460018302840111600160201b8311171561085a57600080fd5b509092509050612329565b6040805193845260208401929092526001600160801b031682820152519081900360600190f35b610161612880565b610413600480360360208110156108aa57600080fd5b503560010b6128a4565b6108bc6128b6565b6040805160029290920b8252519081900360200190f35b6101616128bb565b6108e36128df565b604080516001600160a01b03909816885260029690960b602088015261ffff9485168787015292909316606086015260ff90811660808601529190911660a0840152151560c0830152519081900360e00190f35b610413612932565b61095f6004803603602081101561095557600080fd5b503560020b612938565b604080516001600160801b039099168952600f9790970b602089015287870195909552606087019390935260069190910b60808601526001600160a01b031660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b610292600480360360208110156109d757600080fd5b50356001600160a01b03166129a2565b610161612b13565b7f000000000000000000000000000000000000000000000000000000000000000081565b600080600080600080610a278b8b8b612b29565b949a509298509096509450925090508a15610adc576000851215610a7357610a737f00000000000000000000000000000000000000000000000000000000000000008d87600003613409565b6000610a7d613557565b9050610a8b87878b8b6135f7565b610a93613557565b610a9d8289613697565b1115610ad6576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b50610b78565b6000861215610b1357610b137f00000000000000000000000000000000000000000000000000000000000000008d88600003613409565b6000610b1d6136ad565b9050610b2b87878b8b6135f7565b610b336136ad565b610b3d8288613697565b1115610b76576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b8015610bd457610bd48b610bac577f0000000000000000000000000000000000000000000000000000000000000000610bce565b7f00000000000000000000000000000000000000000000000000000000000000005b8261371c565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e169133916000805160206154de833981519152919081900360a00190a350506002805460ff60e81b1916600160e81b17905550919890975095505050505050565b60045463ffffffff1681565b6003546001600160801b031681565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610cc357600080fd5b505afa158015610cd7573d6000803e3d6000fd5b505050506040513d6020811015610ced57600080fd5b50516001600160a01b03163314610d0357600080fd5b6201518063ffffffff821611801590610d27575060045463ffffffff828116911614155b610d3057600080fd5b6004805463ffffffff831663ffffffff19909116811790915560408051918252517fb5e51602371b0e74f991b6e965cd7d32b4b14c7e6ede6d1298037650a0e1405f9181900360200190a150565b7f000000000000000000000000000000000000000000000000000000000000000081565b600254600160e81b900460ff16610de6576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b191690556003546001600160801b031680610e35576040805162461bcd60e51b81526020600482015260016024820152601360fa1b604482015290519081900360640190fd5b600254600160b81b900461ffff16600080610e4e613557565b90508715610e9557610e68888461ffff16620f42406137b5565b9150610e957f00000000000000000000000000000000000000000000000000000000000000008a8a613409565b600080610ea06136ad565b90508815610ee757610eba898661ffff16620f42406137b5565b9150610ee77f00000000000000000000000000000000000000000000000000000000000000008c8b613409565b336001600160a01b031663a60b0d3c85848b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015610f6957600080fd5b505af1158015610f7d573d6000803e3d6000fd5b5050505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b158015610fdc57600080fd5b505afa158015610ff0573d6000803e3d6000fd5b505050506040513d602081101561100657600080fd5b505190506000611014613557565b9050806110218688613697565b1115611059576040805162461bcd60e51b8152602060048201526002602482015261046360f41b604482015290519081900360640190fd5b84900380156110d357600254600160d81b900460ff16600081156110ad57506103e860ff82168302046110ad7f00000000000000000000000000000000000000000000000000000000000000008583613409565b6110c7818403600160801b8c6001600160801b031661382a565b60008054909101905550505b60006110dd6136ad565b9050806110ea8587613697565b1115611122576040805162461bcd60e51b8152602060048201526002602482015261463160f01b604482015290519081900360640190fd5b839003801561119c57600254600160e01b900460ff166000811561117657506103e860ff82168302046111767f00000000000000000000000000000000000000000000000000000000000000008683613409565b611190818403600160801b8d6001600160801b031661382a565b60018054909101905550505b8d6001600160a01b0316336001600160a01b03167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a350506002805460ff60e81b1916600160e81b179055505050505050505050505050565b6002546000908190600160e81b900460ff1661126b576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560006112853388886138c0565b60038101549091506001600160801b0380821691600160801b9004811690871682106112b157866112b3565b815b9450806001600160801b0316866001600160801b0316116112d457856112d6565b805b93506001600160801b03858517161561139a576003830180546001600160801b0319168684036001600160801b03908116919091178116600160801b87850383160217909155851615611357576113577f00000000000000000000000000000000000000000000000000000000000000008b876001600160801b0316613409565b6001600160801b0384161561139a5761139a7f00000000000000000000000000000000000000000000000000000000000000008b866001600160801b0316613409565b604080516001600160a01b038c1681526001600160801b038088166020830152861681830152905160028a810b92908c900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a450506002805460ff60e81b1916600160e81b1790555090969095509350505050565b60076020526000908152604090208054600182015460028301546003909301546001600160801b038084169463ffffffff600160801b9586900416949092808316929190041686565b60005481565b6e023746e6a58dcb13d4af821b93f06290565b6002546000908190600160e81b900460ff166114c7576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905586156115485760006114e4613557565b90506114f387600087876135f7565b600061150782611501613557565b906138e4565b97508713611542576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b506115ac565b60006115526136ad565b905061156160008887876135f7565b600061156f826115016136ad565b975087136115aa576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b6002805460ff60e81b1916600160e81b17905560008080806115cf8b8b8b612b29565b949a509298509096509450925090508a1561165c57600085121561161b5761161b7f00000000000000000000000000000000000000000000000000000000000000008d87600003613409565b89861215611657576116577f00000000000000000000000000000000000000000000000000000000000000008e6116528d8a6138f4565b613409565b6116ca565b6000861215611693576116937f00000000000000000000000000000000000000000000000000000000000000008d88600003613409565b898512156116ca576116ca7f00000000000000000000000000000000000000000000000000000000000000008e6116528d896138f4565b80156116fe576116fe8b610bac577f0000000000000000000000000000000000000000000000000000000000000000610bce565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e169133916000805160206154de833981519152919081900360a00190a350506002805460ff60e81b1916600160e81b1790555091999098509650505050505050565b60008060008060008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166374eceae6896040518263ffffffff1660e01b81526004018082815260200191505060e06040518083038186803b1580156117e957600080fd5b505afa1580156117fd573d6000803e3d6000fd5b505050506040513d60e081101561181357600080fd5b508051602082015160408301516060840151608085015160a086015160c090960151949e939d50919b50995097509195509350915050565b600254600160e81b900460ff1661188f576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b1580156118fc57600080fd5b505afa158015611910573d6000803e3d6000fd5b505050506040513d602081101561192657600080fd5b50516001600160a01b0316331461193c57600080fd5b60fa60ff831611801590611954575060fa60ff821611155b61195d57600080fd5b6002805460ff60e01b1916600160e01b60ff8481169182029290921760ff60d81b1916600160d81b9286169283021790925560408051918252602082019290925281517f9e22b964b08e25c3aaa72102bb0071c089258fb82d51271a8ddf5c24921356ee929181900390910190a150506002805460ff60e81b1916600160e81b179055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638a2ade586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a3b57600080fd5b505afa158015611a4f573d6000803e3d6000fd5b505050506040513d6020811015611a6557600080fd5b50516001600160a01b03163314611a7b57600080fd5b60048054600160201b600160c01b031916600160201b6001600160a01b038416908102919091179091556040517f915c5369e6580733735d1c2e30ca20dcaa395697a041033c9f35f80f53525e8490600090a250565b600080808484620d89e9600282900b12611b18576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13611b58576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13611b9a576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b611ba261540e565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c0a57600080fd5b50611c1361540e565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c7b57600080fd5b5060028054600160a01b8104820b91600160c81b90910461ffff16908b810b9083900b1215611cd45782600001518460000151038360200151856020015103846040015186604001510398509850985050505050611d7f565b8960020b8260020b1215611d53576000611cec61390a565b9050600080611d148360008787600360009054906101000a90046001600160801b031661390e565b5050915091508560000151876000015183030386602001518860200151830303876040015189604001518603039b509b509b5050505050505050611d7f565b836000015183600001510384602001518460200151038560400151856040015103985098509850505050505b50509250925092565b6060806060807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fd31e988611dc561390a565b6002805460035460405160e086901b6001600160e01b031916815263ffffffff851660048201908152600160a01b8404850b9485900b6044830152600160c81b90930461ffff16606482018190526001600160801b039092166084820181905260a06024830190815260a483018e90528e958e9590949390919060c401876020880280828437600081840152601f19601f82011690508083019250505097505050505050505060006040518083038186803b158015611e8357600080fd5b505afa158015611e97573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526080811015611ec057600080fd5b8101908080516040519392919084600160201b821115611edf57600080fd5b908301906020820185811115611ef457600080fd5b82518660208202830111600160201b82111715611f1057600080fd5b82525081516020918201928201910280838360005b83811015611f3d578181015183820152602001611f25565b5050505090500160405260200180516040519392919084600160201b821115611f6557600080fd5b908301906020820185811115611f7a57600080fd5b82518660208202830111600160201b82111715611f9657600080fd5b82525081516020918201928201910280838360005b83811015611fc3578181015183820152602001611fab565b5050505090500160405260200180516040519392919084600160201b821115611feb57600080fd5b90830190602082018581111561200057600080fd5b82518660208202830111600160201b8211171561201c57600080fd5b82525081516020918201928201910280838360005b83811015612049578181015183820152602001612031565b5050505090500160405260200180516040519392919084600160201b82111561207157600080fd5b90830190602082018581111561208657600080fd5b82518660208202830111600160201b821117156120a257600080fd5b82525081516020918201928201910280838360005b838110156120cf5781810151838201526020016120b7565b50505050905001604052505050935093509350935092959194509250565b6002546000908190600160e81b900460ff16612136576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916815585908590620d89e99082900b12612187576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b136121c7576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612209576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b600080600061222e338b8b6122268c6001600160801b03166139fe565b600003613a14565b9250925092508160000396508060000395508587176000146122af576003830154612262906001600160801b031688613cea565b600384015461228190600160801b90046001600160801b031688613cea565b6003850180546001600160801b03938416928416600160801b029316929092176001600160801b0319161790555b604080516001600160801b038a16815260208101899052808201889052905160028b810b92908d900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a450506002805460ff60e81b1916600160e81b179055509296919550909350505050565b60025460009081908190600160e81b900460ff16612374576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916815588908890620d89e99082900b126123c5576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13612405576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612447576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b6000886001600160801b03161161248a576040805162461bcd60e51b8152602060048201526002602482015261125360f21b604482015290519081900360640190fd5b6000806124c18c8c6124a48d6001600160801b03166139fe565b60028054600160a01b810490910b906001600160a01b0316613d06565b5090975095506000915081905086156124df576124dc613557565b91505b85156124f0576124ed6136ad565b90505b336001600160a01b0316633dd657c588888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561257257600080fd5b505af1158015612586573d6000803e3d6000fd5b5050505060008711156125dd5760008261259e613557565b03925082116125dd576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b851561262d576000816125ee6136ad565b039150811161262d576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b8994508682101561264f5761264c856001600160801b0316838961382a565b94505b8581101561268f57600061266d866001600160801b0316838961382a565b9050856001600160801b0316816001600160801b0316101561268d578095505b505b6000856001600160801b0316116126d6576040805162461bcd60e51b8152602060048083019190915260248201526324a4a61960e11b604482015290519081900360640190fd5b6000806126f68f8f8f6126f18b6001600160801b03166139fe565b613a14565b92509250508382995089111561273b576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b8281985088111561277b576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b5050868211156127b2576127b27f00000000000000000000000000000000000000000000000000000000000000008f898503613409565b858111156127e7576127e77f00000000000000000000000000000000000000000000000000000000000000008f888403613409565b8a60020b8c60020b8e6001600160a01b03167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde33898c8c60405180856001600160a01b03168152602001846001600160801b0316815260200183815260200182815260200194505050505060405180910390a450506002805460ff60e81b1916600160e81b17905550929a919950975095505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60066020526000908152604090205481565b603c90565b7f000000000000000000000000000000000000000000000000000000000000000081565b600280546001600160a01b03811691600160a01b8204900b9061ffff600160b81b8204811691600160c81b81049091169060ff600160d81b8204811691600160e01b8104821691600160e81b9091041687565b60015481565b60056020526000908152604090208054600182015460028301546003909301546001600160801b03831693600160801b909304600f0b9290600681900b90600160381b81046001600160a01b031690600160d81b810463ffffffff1690600160f81b900460ff1688565b6002546001600160a01b0316156129e5576040805162461bcd60e51b8152602060048201526002602482015261414960f01b604482015290519081900360640190fd5b60006129f082613d9e565b905060006129fc61390a565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663475fb80c82846040518363ffffffff1660e01b8152600401808363ffffffff1681526020018260020b815260200192505050600060405180830381600087803b158015612a7557600080fd5b505af1158015612a89573d6000803e3d6000fd5b50506002805485820b62ffffff8116600160a01b0262ffffff60a01b1960ff60e81b196001600160a01b038b166001600160a01b0319909516851716600160e81b17161790925560408051918252602082019290925281517f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c959450908190039091019150a1505050565b600454600160201b90046001600160a01b031681565b6000806000806000806000612b3c61542e565b6002805461ffff600160b81b82048116610160850152600160c81b8204166101a084015260ff60e81b19811682556001600160a01b0381169750600160a01b810490910b955060ff600160d81b8204811691600160e01b8104821691600160e81b9091041680612bd9576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b8c612c10576040805162461bcd60e51b8152602060048201526002602482015261415360f01b604482015290519081900360640190fd5b60008d1361014085015260a084018d90526003546001600160801b03600160801b8204811660208701521696508d15612cb857886001600160a01b03168c6001600160a01b0316108015612c7157506401000276a36001600160a01b038d16115b612ca8576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60005460e0850152828452612d38565b886001600160a01b03168c6001600160a01b0316118015612cf5575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038d16105b612d2c576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60015460e08501528184525b600288810b900b610180850152612d4d61390a565b600454909550600160201b90046001600160a01b031615612e6357600060048054906101000a90046001600160a01b03166001600160a01b0316637f376059876040518263ffffffff1660e01b8152600401808263ffffffff168152602001915050602060405180830381600087803b158015612dc957600080fd5b505af1158015612ddd573d6000803e3d6000fd5b505050506040513d6020811015612df357600080fd5b505190506000816002811115612e0557fe5b1415612e235760048054600160201b600160c01b0319169055612e61565b6001816002811115612e3157fe5b1415612e44576001610120860152612e61565b6002816002811115612e5257fe5b1415612e615760026101208601525b505b6000612e80856101a00151878761018001518b8960200151614089565b9050846101a0015161ffff168161ffff1614612ebf5761ffff81166101a086015260006020860152612eb4868a838b61415d565b61ffff166101608601525b50505050612ecb61549f565b6001600160a01b0387168152612ee36006878e61425f565b15156040830152600290810b900b60208201819052612f019061435f565b6001600160a01b0390811660608301819052612f3f918e918a918e161182151514612f30578360600151612f32565b8c5b888f876101600151614679565b60c085015260a0840152608083015261014083015190975015612f9857612f6f8160c0015182608001510161483d565b8b039a50612f8e612f838260a0015161483d565b60c0840151906138f4565b60c0830152612fd0565b612fa58160a0015161483d565b8b019a50612fca612fbf8260c0015183608001510161483d565b60c084015190614853565b60c08301525b81511561300c57815160c08201516000916103e891612fee91614869565b81612ff557fe5b60c084018051929091049182900390529490940193505b6001600160801b03851615613043576130378160c00151600160801b876001600160801b031661382a565b60e08301805190910190525b80606001516001600160a01b0316876001600160a01b0316141561320a578060400151156131eb5781608001516130c75761308c836000846101800151856101a001518961390e565b50506001600160a01b03166060840152600690810b900b6040830152600160808301528b6130bc576000546130c0565b6001545b6101008301525b600082610120015160028111156130da57fe5b146131625760048054906101000a90046001600160a01b03166001600160a01b03166301342b1982602001518e6040518363ffffffff1660e01b8152600401808360020b8152602001821515815260200192505050600060405180830381600087803b15801561314957600080fd5b505af115801561315d573d6000803e3d6000fd5b505050505b60008c156131a75761319d82602001518460e001518561010001518660600151876040015189600561488d909695949392919063ffffffff16565b60000390506131dd565b6131da82602001518461010001518560e001518660600151876040015189600561488d909695949392919063ffffffff16565b90505b6131e78682614941565b9550505b8b6131fa578060200151613203565b60018160200151035b955061322e565b80516001600160a01b0388811691161461322e5761322787613d9e565b955061325b565b8a158061324c5750896001600160a01b0316876001600160a01b0316145b156132565761325b565b612ecb565b81610140015115158c15151461327c578160c001518b8360a0015103613289565b8a8260a00151038260c001515b6101608401516101a08501516002805461ffff60c81b1916600160c81b61ffff938416021761ffff60b81b1916600160b81b92909316919091029190911762ffffff60a01b1916600160a01b62ffffff8b840b1602176001600160a01b0319166001600160a01b038b81169190911790915560408051631b7297f760e11b81526001600160801b038a16600482015260248101859052604481018490529051939c50919a5087927f0000000000000000000000000000000000000000000000000000000000000000909116916336e52fee916064808301926020929190829003018186803b15801561337a57600080fd5b505afa15801561338e573d6000803e3d6000fd5b505050506040513d60208110156133a457600080fd5b50516020840151600380546001600160801b03948516929093018416600160801b0293909216929092176001600160801b0319169190911790558b156133f15760e08201516000556133fa565b60e08201516001555b50505093975093979195509350565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b602083106134855780518252601f199092019160209182019101613466565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146134e7576040519150601f19603f3d011682016040523d82523d6000602084013e6134ec565b606091505b509150915081801561351a57508051158061351a575080806020019051602081101561351757600080fd5b50515b613550576040805162461bcd60e51b81526020600482015260026024820152612a2360f11b604482015290519081900360640190fd5b5050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156135c657600080fd5b505afa1580156135da573d6000803e3d6000fd5b505050506040513d60208110156135f057600080fd5b5051905090565b336001600160a01b0316632c8958f6858585856040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561367957600080fd5b505af115801561368d573d6000803e3d6000fd5b5050505050505050565b808201828110156136a757600080fd5b92915050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156135c657600080fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561377757600080fd5b505afa15801561378b573d6000803e3d6000fd5b505050506040513d60208110156137a157600080fd5b505190506137b0838284613409565b505050565b60008315806137d0575050828202828482816137cd57fe5b04145b156137f157600082116137e257600080fd5b81810490829006151501613823565b6137fc84848461382a565b90506000828061380857fe5b848609111561382357600019811061381f57600080fd5b6001015b9392505050565b6000838302816000198587098281108382030391505080841161384c57600080fd5b8061385c57508290049050613823565b8385870960008581038616958690049560026003880281188089028203028089028203028089028203028089028203028089028203028089029091030291819003819004600101858411909403939093029190930391909104170290509392505050565b62ffffff9081169116601892831b1790911b17600090815260076020526040902090565b808203828111156136a757600080fd5b808203828113156000831215146136a757600080fd5b4290565b604080516314c5407960e01b815263ffffffff808816600483015286166024820152600285900b604482015261ffff841660648201526001600160801b038316608482015290516000918291829182916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916314c540799160a4808301926080929190829003018186803b1580156139ae57600080fd5b505afa1580156139c2573d6000803e3d6000fd5b505050506040513d60808110156139d857600080fd5b50805160208201516040830151606090930151919c909b50919950975095505050505050565b80600f81900b8114613a0f57600080fd5b919050565b60408051606081018252600280546001600160a01b0381168352600160a01b8104820b90910b6020830152600160c81b900461ffff169181019190915260009081908190613a638888886138c0565b60008054600154929650919080600f89900b15613b2a576000613a8461390a565b9050600080613ab48360008a602001518b60400151600360009054906101000a90046001600160801b031661390e565b505091509150613ae38e89602001518e8a8a86888a600060056149f7909998979695949392919063ffffffff16565b15613af75760019450613af760068f614b92565b6020880151613b12906005908f908f8b8b87898b60016149f7565b15613b265760019350613b2660068e614b92565b5050505b60208501516000908190613b46906005908f908f908a8a614c19565b91509150613b568a8c8484614cbb565b8a600f0b600014613cd95760008b600f0b1215613bd1578315613b9f5760028d810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b8215613bd15760028c810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b6000613be88e8e8e8b602001518c60000151613d06565b919b5099509050600f81900b15613cd75760035460408901516001600160801b0390911690600090613c3b90613c1c61390a565b60208d01516003548690600160801b90046001600160801b0316614089565b90508061ffff168a6040015161ffff1614613caa57613c67613c5b61390a565b8b60200151838561415d565b6002805461ffff60b81b1916600160b81b61ffff938416021761ffff60c81b1916600160c81b92841692909202919091179055600380546001600160801b031690555b613cb4828f614941565b600380546001600160801b0319166001600160801b039290921691909117905550505b505b505050505050509450945094915050565b8082016001600160801b0380841690821610156136a757600080fd5b60008060008760020b8560020b1215613d3b57613d34613d258961435f565b613d2e8961435f565b88614e99565b9250613d93565b8660020b8560020b1215613d7557613d5684613d2e8961435f565b9250613d6b613d648961435f565b8588614ede565b9150859050613d93565b613d90613d818961435f565b613d8a8961435f565b88614ede565b91505b955095509592505050565b60006401000276a36001600160a01b03831610801590613dda575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038316105b613e0f576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b600160201b600160c01b03602083901b166001600160801b03811160071b81811c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c97908811961790941790921717909117171760808110613ea157607f810383901c9150613eab565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d607f198f0160401b60c09190911c6001603f1b161760c19b909b1c6001603e1b169a909a1760c29990991c6001603d1b169890981760c39790971c6001603c1b169690961760c49590951c6001603b1b169490941760c59390931c6001603a1b169290921760c69190911c600160391b161760c79190911c600160381b161760c89190911c600160371b161760c99190911c600160361b161760ca9190911c600160351b161760cb9190911c600160341b161760cc9190911c600160331b161760cd9190911c600160321b1617693627a301d71055774c8581026f028f6481ab7f045a5af012a19d003aa9198101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b1461407a57886001600160a01b031661405e8261435f565b6001600160a01b031611156140735781614075565b805b61407c565b815b9998505050505050505050565b60408051630eea437960e11b815261ffff8716600482015263ffffffff86166024820152600285900b60448201526001600160801b0380851660648301528316608482015290516000916001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691631dd486f29160a48082019260209290919082900301818787803b15801561412557600080fd5b505af1158015614139573d6000803e3d6000fd5b505050506040513d602081101561414f57600080fd5b505190505b95945050505050565b6040805163c53a182f60e01b815263ffffffff86166004820152600285900b602482015261ffff841660448201526001600160801b038316606482015290516000916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163c53a182f91608480820192602092909190829003018186803b1580156141f057600080fd5b505afa158015614204573d6000803e3d6000fd5b505050506040513d602081101561421a57600080fd5b50516040805161ffff8316815290519192507f598b9f043c813aa6be3426ca60d1c65d17256312890be5118dab55b0775ebe2a919081900360200190a1949350505050565b6000806000603c9050808507151960008612168186050394505082156142f657600884901c600181810b900b60009081526020879052604090205460ff868116918282039091161b80156142d7576142b681614f0e565b60ff0360ff16870396506142c987614f51565b600194509450505050614357565b8160ff16870396506142e887614f51565b600094509450505050614357565b6001938401600881901c80860b90950b60009081526020879052604090205490949060ff861690811c801561434357614333818260000316614f89565b60ff16870196506142c987614f51565b8160ff0360ff16870196506142e887614f51565b935093915050565b6000600282810b60171d90818418829003900b620d89e88111156143ae576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b6000600182166143c257600160801b6143d4565b6ffffcb933bd6fad37aa2d162d1a5940015b6001600160881b0316905060028216156143fe576ffff97272373d413259a46990580e213a0260801c5b600482161561441d576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b600882161561443c576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b601082161561445b576fffcb9843d60f6159c9db58835c9266440260801c5b602082161561447a576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615614499576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156144b8576ffe5dee046a99a2a811c461f1969c30530260801c5b6101008216156144d8576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b6102008216156144f8576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615614518576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615614538576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615614558576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615614578576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615614598576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156145b8576f31be135f97d08fd981231505542fcfa60260801c5b620100008216156145d9576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b620200008216156145f9576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614618576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614635576b048a170391f7dc42444e8fa20260801c5b60008560020b131561465057806000198161464c57fe5b0490505b600160201b810615614663576001614666565b60005b60ff16602082901c019350505050919050565b6000806000806154db8a61468f5761509d614693565b6150ac5b90506000871261477f5760006146bb888861ffff16620f42400362ffffff16620f424061382a565b90506146cc8a8c8b8563ffffffff16565b94508481106146f9578995506146f28561ffff891662ffffff620f4240829003166137b5565b9250614758565b6147058b8a838f6150bb565b9550856001600160a01b03168a6001600160a01b03161461473b5761472f868c8b8563ffffffff16565b94508488039250614758565b6147558561ffff891662ffffff620f4240829003166137b5565b92505b614777868c8b8f61476b576150cb61476f565b6150da5b63ffffffff16565b93505061482f565b6154db8b61478f576150cb614793565b6150da5b90506147a48a8c8b8463ffffffff16565b93508760000397508388106147bb57899550614800565b6147c78b8a8a8f6150e9565b9550856001600160a01b03168a6001600160a01b0316146147f4576147f1868c8b8463ffffffff16565b93505b87841115614800578793505b61480f868c8b8563ffffffff16565b945061482b8561ffff891662ffffff620f4240829003166137b5565b9250505b509650965096509692505050565b6000600160ff1b821061484f57600080fd5b5090565b818101828112156000831215146136a757600080fd5b60008215806148845750508181028183828161488157fe5b04145b6136a757600080fd5b600286810b810b60009081526020899052604090206003810180546001600160a01b03600160381b63ffffffff600160d81b808504821689039091160263ffffffff60d81b199093169290921782810482168903909116909102600160381b600160d81b031990911617600681810b8703900b66ffffffffffffff1666ffffffffffffff199091161790559081018054860390556001810180548703905554600160801b9004600f0b979650505050505050565b60008082600f0b12156149a657826001600160801b03168260000384039150816001600160801b0316106149a1576040805162461bcd60e51b81526020600482015260026024820152614c5360f01b604482015290519081900360640190fd5b6136a7565b826001600160801b03168284019150816001600160801b031610156136a7576040805162461bcd60e51b81526020600482015260026024820152614c4160f01b604482015290519081900360640190fd5b600289810b900b600090815260208b9052604081208054600160801b8104600f0b906001600160801b031683614a2d828d614941565b90506e023746e6a58dcb13d4af821b93f0636001600160801b03821610614a80576040805162461bcd60e51b81526020600482015260026024820152614c4f60f01b604482015290519081900360640190fd5b85614aa357614a9e614a99600f85810b908f900b614853565b6139fe565b614ab7565b614ab7614a99600f85810b908f900b6138f4565b84546001600160801b03838116600f9390930b8116600160801b02918116919091176001600160801b03191682178655901595508216614b8057841594508c60020b8e60020b13614b6857600184018b9055600284018a9055600384018054600160381b600160d81b031916600160381b6001600160a01b038c16021766ffffffffffffff191666ffffffffffffff60068b900b161763ffffffff60d81b1916600160d81b63ffffffff8a16021790555b6003840180546001600160f81b0316600160f81b1790555b505050509a9950505050505050505050565b603c600282900b0760020b15614be4576040805162461bcd60e51b81526020600482015260126024820152711d1a58dac81a5cc81b9bdd081cdc1858d95960721b604482015290519081900360640190fd5b603c60029190910b05600881901c600190810b810b60009081526020939093526040909220805460ff9092169290921b189055565b600285810b810b60009081526020889052604080822087840b80850b84529183209293849391929088900b1215614c93578860020b8760020b12614c6e57816001015486039350816002015485039250614c7d565b81600101549350816002015492505b6001810154600282015494039390920391614cae565b81600101548160010154039350816002015481600201540392505b5050965096945050505050565b83546001600160801b03811690600160801b900463ffffffff16600f85900b614d26576000826001600160801b031611614d21576040805162461bcd60e51b815260206004820152600260248201526104e560f41b604482015290519081900360640190fd5b614ddf565b600085600f0b1215614d655760045463ffffffff168015614d63578063ffffffff1682614d5161390a565b0363ffffffff161015614d6357600080fd5b505b6000614d718387614941565b9050806000826001600160801b031611614d8c576000614da5565b600087600f0b13614d9d5782614da5565b614da561390a565b88546001600160801b039092166001600160801b031963ffffffff909216600160801b0263ffffffff60801b199093169290921716178755505b600186015460028701546000868314614e165760018901879055614e138388036001600160801b038716600160801b61382a565b90505b6000828714614e435760028a01879055614e408388036001600160801b038816600160801b61382a565b90505b6001600160801b038282171615614e8d5760038a0180546001600160801b031981166001600160801b039182168501821617808216600160801b9182900483168501909216021790555b50505050505050505050565b60008082600f0b1215614ec657614ebe614eb985858560000360006150f9565b61483d565b600003614ed6565b614ed6614eb985858560016150f9565b949350505050565b60008082600f0b1215614efe57614ebe614eb98585856000036000615188565b614ed6614eb98585856001615188565b600181811c909117600281901c17600481901c17600881901c17601081901c17602081901c17604081901c17608081901c179081901c900360006136a782614f89565b603c8102620d89e719600282900b1215614f705750620d89e719613a0f565b620d89e8600282900b1315613a0f5750620d89e8919050565b7f55555555555555555555555555555555555555555555555555555555555555558116156001600160801b0382161560071b176001600160401b03600160801b03600160c01b0382161560061b177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff82161560051b177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff82161560041b177eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff82161560031b177f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f82161560021b177f3333333333333333333333333333333333333333333333333333333333333333919091161560011b1790565b6000614ed68385846001615188565b6000614ed684848460016150f9565b60006141548585858560016151f9565b6000614ed683858460006150f9565b6000614ed68484846000615188565b60006141548585858560006151f9565b60006001600160a01b038585038116908516811061511657600080fd5b600160601b600160e01b03606085901b168361515757866001600160a01b031661514a8383896001600160a01b031661382a565b8161515157fe5b0461517d565b61517d61516e8383896001600160a01b03166137b5565b886001600160a01b03166153ed565b979650505050505050565b6000846001600160a01b0316846001600160a01b031610156151a957600080fd5b6001600160a01b0385850316826151d7576151d281856001600160801b0316600160601b61382a565b6151ef565b6151ef81856001600160801b0316600160601b6137b5565b9695505050505050565b600080866001600160a01b03161161521057600080fd5b6000856001600160801b03161161522657600080fd5b8115158315151415615318578361523e575084614154565b600160601b600160e01b03606086901b1682156152cc576001600160a01b0387168581029086828161526c57fe5b04141561529d5781810182811061529b57615291838a6001600160a01b0316836137b5565b9350505050614154565b505b6152c3826152be888b6001600160a01b031686816152b757fe5b0490613697565b6153ed565b92505050614154565b6001600160a01b038716858102908682816152e357fe5b04146152ee57600080fd5b8082116152fa57600080fd5b6152c3615313838a6001600160a01b03168486036137b5565b6153f8565b811561537f576153786153136001600160a01b038611156153505761534b86600160601b896001600160801b031661382a565b615368565b6001600160801b038716606087901b8161536657fe5b045b6001600160a01b03891690613697565b9050614154565b60006001600160a01b038511156153ad576153a885600160601b886001600160801b03166137b5565b6153c4565b6153c4606086901b6001600160801b0388166153ed565b905080876001600160a01b0316116153db57600080fd5b6001600160a01b038716039050614154565b808204910615150190565b806001600160a01b0381168114613a0f57600080fd5b604080516060810182526000808252602082018190529181019190915290565b604080516101c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052906101208201908152600060208201819052604082018190526060820181905260809091015290565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565bfefec42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a164736f6c6343000706000a
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101545760003560e01c80630dfe168114610159578063128acb081461017d57806317e25b3c1461022a5780631a6865021461024b578063289fe9b01461026f57806329047dfa14610294578063490e6cbc1461029c5780634f1eb3d814610326578063514ea4bf146103a65780636378ae441461040b57806370cf754a14610425578063713346941461042d57806374eceae6146104ca5780637c0112b7146105465780637c1fe0c81461056e578063920c34e5146105945780639d3a5241146105ef578063a34123a714610780578063aafe29c0146107ba578063c45a01551461088c578063c677e3e014610894578063d0c93a7c146108b4578063d21220a7146108d3578063e76c01e4146108db578063ecdecf4214610937578063f30dba931461093f578063f637731d146109c1578063facb0eb1146109e7575b600080fd5b6101616109ef565b604080516001600160a01b039092168252519081900360200190f35b610211600480360360a081101561019357600080fd5b6001600160a01b0382358116926020810135151592604082013592606083013516919081019060a081016080820135600160201b8111156101d357600080fd5b8201836020820111156101e557600080fd5b803590602001918460018302840111600160201b8311171561020657600080fd5b509092509050610a13565b6040805192835260208301919091528051918290030190f35b610232610c4f565b6040805163ffffffff9092168252519081900360200190f35b610253610c5b565b604080516001600160801b039092168252519081900360200190f35b6102926004803603602081101561028557600080fd5b503563ffffffff16610c6a565b005b610161610d7e565b610292600480360360808110156102b257600080fd5b6001600160a01b038235169160208101359160408201359190810190608081016060820135600160201b8111156102e857600080fd5b8201836020820111156102fa57600080fd5b803590602001918460018302840111600160201b8311171561031b57600080fd5b509092509050610da2565b610377600480360360a081101561033c57600080fd5b506001600160a01b03813516906020810135600290810b91604081013590910b906001600160801b0360608201358116916080013516611222565b60405180836001600160801b03168152602001826001600160801b031681526020019250505060405180910390f35b6103c3600480360360208110156103bc57600080fd5b503561141c565b604080516001600160801b03978816815263ffffffff90961660208701528581019490945260608501929092528416608084015290921660a082015290519081900360c00190f35b610413611465565b60408051918252519081900360200190f35b61025361146b565b610211600480360360c081101561044357600080fd5b6001600160a01b0382358116926020810135821692604082013515159260608301359260808101359091169181019060c0810160a0820135600160201b81111561048c57600080fd5b82018360208201111561049e57600080fd5b803590602001918460018302840111600160201b831117156104bf57600080fd5b50909250905061147e565b6104e7600480360360208110156104e057600080fd5b503561177a565b60408051971515885263ffffffff909616602088015260069490940b868601526001600160a01b0390921660608601526001600160581b0316608085015260020b60a08401526001600160901b031660c0830152519081900360e00190f35b6102926004803603604081101561055c57600080fd5b5060ff8135811691602001351661184b565b6102926004803603602081101561058457600080fd5b50356001600160a01b03166119e2565b6105be600480360360408110156105aa57600080fd5b508035600290810b9160200135900b611ad1565b6040805160069490940b84526001600160a01b03909216602084015263ffffffff1682820152519081900360600190f35b61065d6004803603602081101561060557600080fd5b810190602081018135600160201b81111561061f57600080fd5b82018360208201111561063157600080fd5b803590602001918460208302840111600160201b8311171561065257600080fd5b509092509050611d88565b6040518080602001806020018060200180602001858103855289818151815260200191508051906020019060200280838360005b838110156106a9578181015183820152602001610691565b50505050905001858103845288818151815260200191508051906020019060200280838360005b838110156106e85781810151838201526020016106d0565b50505050905001858103835287818151815260200191508051906020019060200280838360005b8381101561072757818101518382015260200161070f565b50505050905001858103825286818151815260200191508051906020019060200280838360005b8381101561076657818101518382015260200161074e565b505050509050019850505050505050505060405180910390f35b6102116004803603606081101561079657600080fd5b508035600290810b91602081013590910b90604001356001600160801b03166120ed565b610865600480360360c08110156107d057600080fd5b6001600160a01b0382358116926020810135909116916040820135600290810b92606081013590910b916001600160801b03608083013516919081019060c0810160a0820135600160201b81111561082757600080fd5b82018360208201111561083957600080fd5b803590602001918460018302840111600160201b8311171561085a57600080fd5b509092509050612329565b6040805193845260208401929092526001600160801b031682820152519081900360600190f35b610161612880565b610413600480360360208110156108aa57600080fd5b503560010b6128a4565b6108bc6128b6565b6040805160029290920b8252519081900360200190f35b6101616128bb565b6108e36128df565b604080516001600160a01b03909816885260029690960b602088015261ffff9485168787015292909316606086015260ff90811660808601529190911660a0840152151560c0830152519081900360e00190f35b610413612932565b61095f6004803603602081101561095557600080fd5b503560020b612938565b604080516001600160801b039099168952600f9790970b602089015287870195909552606087019390935260069190910b60808601526001600160a01b031660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b610292600480360360208110156109d757600080fd5b50356001600160a01b03166129a2565b610161612b13565b7f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a06381565b600080600080600080610a278b8b8b612b29565b949a509298509096509450925090508a15610adc576000851215610a7357610a737f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f8d87600003613409565b6000610a7d613557565b9050610a8b87878b8b6135f7565b610a93613557565b610a9d8289613697565b1115610ad6576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b50610b78565b6000861215610b1357610b137f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0638d88600003613409565b6000610b1d6136ad565b9050610b2b87878b8b6135f7565b610b336136ad565b610b3d8288613697565b1115610b76576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b8015610bd457610bd48b610bac577f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f610bce565b7f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0635b8261371c565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e169133916000805160206154de833981519152919081900360a00190a350506002805460ff60e81b1916600160e81b17905550919890975095505050505050565b60045463ffffffff1681565b6003546001600160801b031681565b7f000000000000000000000000411b0facc3489691f28ad58c47006af5e3ab3a286001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610cc357600080fd5b505afa158015610cd7573d6000803e3d6000fd5b505050506040513d6020811015610ced57600080fd5b50516001600160a01b03163314610d0357600080fd5b6201518063ffffffff821611801590610d27575060045463ffffffff828116911614155b610d3057600080fd5b6004805463ffffffff831663ffffffff19909116811790915560408051918252517fb5e51602371b0e74f991b6e965cd7d32b4b14c7e6ede6d1298037650a0e1405f9181900360200190a150565b7f000000000000000000000000b1aad177126d9094a1c410f591132aa9dddf940581565b600254600160e81b900460ff16610de6576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b191690556003546001600160801b031680610e35576040805162461bcd60e51b81526020600482015260016024820152601360fa1b604482015290519081900360640190fd5b600254600160b81b900461ffff16600080610e4e613557565b90508715610e9557610e68888461ffff16620f42406137b5565b9150610e957f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0638a8a613409565b600080610ea06136ad565b90508815610ee757610eba898661ffff16620f42406137b5565b9150610ee77f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f8c8b613409565b336001600160a01b031663a60b0d3c85848b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015610f6957600080fd5b505af1158015610f7d573d6000803e3d6000fd5b5050505060007f000000000000000000000000411b0facc3489691f28ad58c47006af5e3ab3a286001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b158015610fdc57600080fd5b505afa158015610ff0573d6000803e3d6000fd5b505050506040513d602081101561100657600080fd5b505190506000611014613557565b9050806110218688613697565b1115611059576040805162461bcd60e51b8152602060048201526002602482015261046360f41b604482015290519081900360640190fd5b84900380156110d357600254600160d81b900460ff16600081156110ad57506103e860ff82168302046110ad7f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0638583613409565b6110c7818403600160801b8c6001600160801b031661382a565b60008054909101905550505b60006110dd6136ad565b9050806110ea8587613697565b1115611122576040805162461bcd60e51b8152602060048201526002602482015261463160f01b604482015290519081900360640190fd5b839003801561119c57600254600160e01b900460ff166000811561117657506103e860ff82168302046111767f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f8683613409565b611190818403600160801b8d6001600160801b031661382a565b60018054909101905550505b8d6001600160a01b0316336001600160a01b03167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a350506002805460ff60e81b1916600160e81b179055505050505050505050505050565b6002546000908190600160e81b900460ff1661126b576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560006112853388886138c0565b60038101549091506001600160801b0380821691600160801b9004811690871682106112b157866112b3565b815b9450806001600160801b0316866001600160801b0316116112d457856112d6565b805b93506001600160801b03858517161561139a576003830180546001600160801b0319168684036001600160801b03908116919091178116600160801b87850383160217909155851615611357576113577f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0638b876001600160801b0316613409565b6001600160801b0384161561139a5761139a7f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f8b866001600160801b0316613409565b604080516001600160a01b038c1681526001600160801b038088166020830152861681830152905160028a810b92908c900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a450506002805460ff60e81b1916600160e81b1790555090969095509350505050565b60076020526000908152604090208054600182015460028301546003909301546001600160801b038084169463ffffffff600160801b9586900416949092808316929190041686565b60005481565b6e023746e6a58dcb13d4af821b93f06290565b6002546000908190600160e81b900460ff166114c7576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905586156115485760006114e4613557565b90506114f387600087876135f7565b600061150782611501613557565b906138e4565b97508713611542576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b506115ac565b60006115526136ad565b905061156160008887876135f7565b600061156f826115016136ad565b975087136115aa576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b6002805460ff60e81b1916600160e81b17905560008080806115cf8b8b8b612b29565b949a509298509096509450925090508a1561165c57600085121561161b5761161b7f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f8d87600003613409565b89861215611657576116577f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0638e6116528d8a6138f4565b613409565b6116ca565b6000861215611693576116937f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0638d88600003613409565b898512156116ca576116ca7f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f8e6116528d896138f4565b80156116fe576116fe8b610bac577f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f610bce565b60408051878152602081018790526001600160a01b03868116828401526001600160801b0385166060830152600286900b60808301529151918e169133916000805160206154de833981519152919081900360a00190a350506002805460ff60e81b1916600160e81b1790555091999098509650505050505050565b60008060008060008060007f000000000000000000000000b1aad177126d9094a1c410f591132aa9dddf94056001600160a01b03166374eceae6896040518263ffffffff1660e01b81526004018082815260200191505060e06040518083038186803b1580156117e957600080fd5b505afa1580156117fd573d6000803e3d6000fd5b505050506040513d60e081101561181357600080fd5b508051602082015160408301516060840151608085015160a086015160c090960151949e939d50919b50995097509195509350915050565b600254600160e81b900460ff1661188f576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916905560408051638da5cb5b60e01b815290516001600160a01b037f000000000000000000000000411b0facc3489691f28ad58c47006af5e3ab3a281691638da5cb5b916004808301926020929190829003018186803b1580156118fc57600080fd5b505afa158015611910573d6000803e3d6000fd5b505050506040513d602081101561192657600080fd5b50516001600160a01b0316331461193c57600080fd5b60fa60ff831611801590611954575060fa60ff821611155b61195d57600080fd5b6002805460ff60e01b1916600160e01b60ff8481169182029290921760ff60d81b1916600160d81b9286169283021790925560408051918252602082019290925281517f9e22b964b08e25c3aaa72102bb0071c089258fb82d51271a8ddf5c24921356ee929181900390910190a150506002805460ff60e81b1916600160e81b179055565b7f000000000000000000000000411b0facc3489691f28ad58c47006af5e3ab3a286001600160a01b0316638a2ade586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a3b57600080fd5b505afa158015611a4f573d6000803e3d6000fd5b505050506040513d6020811015611a6557600080fd5b50516001600160a01b03163314611a7b57600080fd5b60048054600160201b600160c01b031916600160201b6001600160a01b038416908102919091179091556040517f915c5369e6580733735d1c2e30ca20dcaa395697a041033c9f35f80f53525e8490600090a250565b600080808484620d89e9600282900b12611b18576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13611b58576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13611b9a576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b611ba261540e565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c0a57600080fd5b50611c1361540e565b600288810b900b6000908152600560209081526040918290206003810154600160d81b810463ffffffff1693850193909352600160381b83046001600160a01b031691840191909152600682810b810b900b835290600160f81b900460ff16611c7b57600080fd5b5060028054600160a01b8104820b91600160c81b90910461ffff16908b810b9083900b1215611cd45782600001518460000151038360200151856020015103846040015186604001510398509850985050505050611d7f565b8960020b8260020b1215611d53576000611cec61390a565b9050600080611d148360008787600360009054906101000a90046001600160801b031661390e565b5050915091508560000151876000015183030386602001518860200151830303876040015189604001518603039b509b509b5050505050505050611d7f565b836000015183600001510384602001518460200151038560400151856040015103985098509850505050505b50509250925092565b6060806060807f000000000000000000000000b1aad177126d9094a1c410f591132aa9dddf94056001600160a01b031663fd31e988611dc561390a565b6002805460035460405160e086901b6001600160e01b031916815263ffffffff851660048201908152600160a01b8404850b9485900b6044830152600160c81b90930461ffff16606482018190526001600160801b039092166084820181905260a06024830190815260a483018e90528e958e9590949390919060c401876020880280828437600081840152601f19601f82011690508083019250505097505050505050505060006040518083038186803b158015611e8357600080fd5b505afa158015611e97573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526080811015611ec057600080fd5b8101908080516040519392919084600160201b821115611edf57600080fd5b908301906020820185811115611ef457600080fd5b82518660208202830111600160201b82111715611f1057600080fd5b82525081516020918201928201910280838360005b83811015611f3d578181015183820152602001611f25565b5050505090500160405260200180516040519392919084600160201b821115611f6557600080fd5b908301906020820185811115611f7a57600080fd5b82518660208202830111600160201b82111715611f9657600080fd5b82525081516020918201928201910280838360005b83811015611fc3578181015183820152602001611fab565b5050505090500160405260200180516040519392919084600160201b821115611feb57600080fd5b90830190602082018581111561200057600080fd5b82518660208202830111600160201b8211171561201c57600080fd5b82525081516020918201928201910280838360005b83811015612049578181015183820152602001612031565b5050505090500160405260200180516040519392919084600160201b82111561207157600080fd5b90830190602082018581111561208657600080fd5b82518660208202830111600160201b821117156120a257600080fd5b82525081516020918201928201910280838360005b838110156120cf5781810151838201526020016120b7565b50505050905001604052505050935093509350935092959194509250565b6002546000908190600160e81b900460ff16612136576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916815585908590620d89e99082900b12612187576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b136121c7576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612209576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b600080600061222e338b8b6122268c6001600160801b03166139fe565b600003613a14565b9250925092508160000396508060000395508587176000146122af576003830154612262906001600160801b031688613cea565b600384015461228190600160801b90046001600160801b031688613cea565b6003850180546001600160801b03938416928416600160801b029316929092176001600160801b0319161790555b604080516001600160801b038a16815260208101899052808201889052905160028b810b92908d900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a450506002805460ff60e81b1916600160e81b179055509296919550909350505050565b60025460009081908190600160e81b900460ff16612374576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6002805460ff60e81b1916815588908890620d89e99082900b126123c5576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b8160020b8160020b13612405576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e819600283900b13612447576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b6000886001600160801b03161161248a576040805162461bcd60e51b8152602060048201526002602482015261125360f21b604482015290519081900360640190fd5b6000806124c18c8c6124a48d6001600160801b03166139fe565b60028054600160a01b810490910b906001600160a01b0316613d06565b5090975095506000915081905086156124df576124dc613557565b91505b85156124f0576124ed6136ad565b90505b336001600160a01b0316633dd657c588888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561257257600080fd5b505af1158015612586573d6000803e3d6000fd5b5050505060008711156125dd5760008261259e613557565b03925082116125dd576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b851561262d576000816125ee6136ad565b039150811161262d576040805162461bcd60e51b815260206004808301919091526024820152634949414d60e01b604482015290519081900360640190fd5b8994508682101561264f5761264c856001600160801b0316838961382a565b94505b8581101561268f57600061266d866001600160801b0316838961382a565b9050856001600160801b0316816001600160801b0316101561268d578095505b505b6000856001600160801b0316116126d6576040805162461bcd60e51b8152602060048083019190915260248201526324a4a61960e11b604482015290519081900360640190fd5b6000806126f68f8f8f6126f18b6001600160801b03166139fe565b613a14565b92509250508382995089111561273b576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b8281985088111561277b576040805162461bcd60e51b815260206004820152600560248201526424a4a0a69960d91b604482015290519081900360640190fd5b5050868211156127b2576127b27f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0638f898503613409565b858111156127e7576127e77f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f8f888403613409565b8a60020b8c60020b8e6001600160a01b03167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde33898c8c60405180856001600160a01b03168152602001846001600160801b0316815260200183815260200182815260200194505050505060405180910390a450506002805460ff60e81b1916600160e81b17905550929a919950975095505050505050565b7f000000000000000000000000411b0facc3489691f28ad58c47006af5e3ab3a2881565b60066020526000908152604090205481565b603c90565b7f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f81565b600280546001600160a01b03811691600160a01b8204900b9061ffff600160b81b8204811691600160c81b81049091169060ff600160d81b8204811691600160e01b8104821691600160e81b9091041687565b60015481565b60056020526000908152604090208054600182015460028301546003909301546001600160801b03831693600160801b909304600f0b9290600681900b90600160381b81046001600160a01b031690600160d81b810463ffffffff1690600160f81b900460ff1688565b6002546001600160a01b0316156129e5576040805162461bcd60e51b8152602060048201526002602482015261414960f01b604482015290519081900360640190fd5b60006129f082613d9e565b905060006129fc61390a565b90507f000000000000000000000000b1aad177126d9094a1c410f591132aa9dddf94056001600160a01b031663475fb80c82846040518363ffffffff1660e01b8152600401808363ffffffff1681526020018260020b815260200192505050600060405180830381600087803b158015612a7557600080fd5b505af1158015612a89573d6000803e3d6000fd5b50506002805485820b62ffffff8116600160a01b0262ffffff60a01b1960ff60e81b196001600160a01b038b166001600160a01b0319909516851716600160e81b17161790925560408051918252602082019290925281517f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c959450908190039091019150a1505050565b600454600160201b90046001600160a01b031681565b6000806000806000806000612b3c61542e565b6002805461ffff600160b81b82048116610160850152600160c81b8204166101a084015260ff60e81b19811682556001600160a01b0381169750600160a01b810490910b955060ff600160d81b8204811691600160e01b8104821691600160e81b9091041680612bd9576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b8c612c10576040805162461bcd60e51b8152602060048201526002602482015261415360f01b604482015290519081900360640190fd5b60008d1361014085015260a084018d90526003546001600160801b03600160801b8204811660208701521696508d15612cb857886001600160a01b03168c6001600160a01b0316108015612c7157506401000276a36001600160a01b038d16115b612ca8576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60005460e0850152828452612d38565b886001600160a01b03168c6001600160a01b0316118015612cf5575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038d16105b612d2c576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b60015460e08501528184525b600288810b900b610180850152612d4d61390a565b600454909550600160201b90046001600160a01b031615612e6357600060048054906101000a90046001600160a01b03166001600160a01b0316637f376059876040518263ffffffff1660e01b8152600401808263ffffffff168152602001915050602060405180830381600087803b158015612dc957600080fd5b505af1158015612ddd573d6000803e3d6000fd5b505050506040513d6020811015612df357600080fd5b505190506000816002811115612e0557fe5b1415612e235760048054600160201b600160c01b0319169055612e61565b6001816002811115612e3157fe5b1415612e44576001610120860152612e61565b6002816002811115612e5257fe5b1415612e615760026101208601525b505b6000612e80856101a00151878761018001518b8960200151614089565b9050846101a0015161ffff168161ffff1614612ebf5761ffff81166101a086015260006020860152612eb4868a838b61415d565b61ffff166101608601525b50505050612ecb61549f565b6001600160a01b0387168152612ee36006878e61425f565b15156040830152600290810b900b60208201819052612f019061435f565b6001600160a01b0390811660608301819052612f3f918e918a918e161182151514612f30578360600151612f32565b8c5b888f876101600151614679565b60c085015260a0840152608083015261014083015190975015612f9857612f6f8160c0015182608001510161483d565b8b039a50612f8e612f838260a0015161483d565b60c0840151906138f4565b60c0830152612fd0565b612fa58160a0015161483d565b8b019a50612fca612fbf8260c0015183608001510161483d565b60c084015190614853565b60c08301525b81511561300c57815160c08201516000916103e891612fee91614869565b81612ff557fe5b60c084018051929091049182900390529490940193505b6001600160801b03851615613043576130378160c00151600160801b876001600160801b031661382a565b60e08301805190910190525b80606001516001600160a01b0316876001600160a01b0316141561320a578060400151156131eb5781608001516130c75761308c836000846101800151856101a001518961390e565b50506001600160a01b03166060840152600690810b900b6040830152600160808301528b6130bc576000546130c0565b6001545b6101008301525b600082610120015160028111156130da57fe5b146131625760048054906101000a90046001600160a01b03166001600160a01b03166301342b1982602001518e6040518363ffffffff1660e01b8152600401808360020b8152602001821515815260200192505050600060405180830381600087803b15801561314957600080fd5b505af115801561315d573d6000803e3d6000fd5b505050505b60008c156131a75761319d82602001518460e001518561010001518660600151876040015189600561488d909695949392919063ffffffff16565b60000390506131dd565b6131da82602001518461010001518560e001518660600151876040015189600561488d909695949392919063ffffffff16565b90505b6131e78682614941565b9550505b8b6131fa578060200151613203565b60018160200151035b955061322e565b80516001600160a01b0388811691161461322e5761322787613d9e565b955061325b565b8a158061324c5750896001600160a01b0316876001600160a01b0316145b156132565761325b565b612ecb565b81610140015115158c15151461327c578160c001518b8360a0015103613289565b8a8260a00151038260c001515b6101608401516101a08501516002805461ffff60c81b1916600160c81b61ffff938416021761ffff60b81b1916600160b81b92909316919091029190911762ffffff60a01b1916600160a01b62ffffff8b840b1602176001600160a01b0319166001600160a01b038b81169190911790915560408051631b7297f760e11b81526001600160801b038a16600482015260248101859052604481018490529051939c50919a5087927f000000000000000000000000b1aad177126d9094a1c410f591132aa9dddf9405909116916336e52fee916064808301926020929190829003018186803b15801561337a57600080fd5b505afa15801561338e573d6000803e3d6000fd5b505050506040513d60208110156133a457600080fd5b50516020840151600380546001600160801b03948516929093018416600160801b0293909216929092176001600160801b0319169190911790558b156133f15760e08201516000556133fa565b60e08201516001555b50505093975093979195509350565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b602083106134855780518252601f199092019160209182019101613466565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146134e7576040519150601f19603f3d011682016040523d82523d6000602084013e6134ec565b606091505b509150915081801561351a57508051158061351a575080806020019051602081101561351757600080fd5b50515b613550576040805162461bcd60e51b81526020600482015260026024820152612a2360f11b604482015290519081900360640190fd5b5050505050565b60007f0000000000000000000000008f3cf7ad23cd3cadbd9735aff958023239c6a0636001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156135c657600080fd5b505afa1580156135da573d6000803e3d6000fd5b505050506040513d60208110156135f057600080fd5b5051905090565b336001600160a01b0316632c8958f6858585856040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561367957600080fd5b505af115801561368d573d6000803e3d6000fd5b5050505050505050565b808201828110156136a757600080fd5b92915050565b60007f000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156135c657600080fd5b60007f000000000000000000000000411b0facc3489691f28ad58c47006af5e3ab3a286001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561377757600080fd5b505afa15801561378b573d6000803e3d6000fd5b505050506040513d60208110156137a157600080fd5b505190506137b0838284613409565b505050565b60008315806137d0575050828202828482816137cd57fe5b04145b156137f157600082116137e257600080fd5b81810490829006151501613823565b6137fc84848461382a565b90506000828061380857fe5b848609111561382357600019811061381f57600080fd5b6001015b9392505050565b6000838302816000198587098281108382030391505080841161384c57600080fd5b8061385c57508290049050613823565b8385870960008581038616958690049560026003880281188089028203028089028203028089028203028089028203028089028203028089029091030291819003819004600101858411909403939093029190930391909104170290509392505050565b62ffffff9081169116601892831b1790911b17600090815260076020526040902090565b808203828111156136a757600080fd5b808203828113156000831215146136a757600080fd5b4290565b604080516314c5407960e01b815263ffffffff808816600483015286166024820152600285900b604482015261ffff841660648201526001600160801b038316608482015290516000918291829182916001600160a01b037f000000000000000000000000b1aad177126d9094a1c410f591132aa9dddf940516916314c540799160a4808301926080929190829003018186803b1580156139ae57600080fd5b505afa1580156139c2573d6000803e3d6000fd5b505050506040513d60808110156139d857600080fd5b50805160208201516040830151606090930151919c909b50919950975095505050505050565b80600f81900b8114613a0f57600080fd5b919050565b60408051606081018252600280546001600160a01b0381168352600160a01b8104820b90910b6020830152600160c81b900461ffff169181019190915260009081908190613a638888886138c0565b60008054600154929650919080600f89900b15613b2a576000613a8461390a565b9050600080613ab48360008a602001518b60400151600360009054906101000a90046001600160801b031661390e565b505091509150613ae38e89602001518e8a8a86888a600060056149f7909998979695949392919063ffffffff16565b15613af75760019450613af760068f614b92565b6020880151613b12906005908f908f8b8b87898b60016149f7565b15613b265760019350613b2660068e614b92565b5050505b60208501516000908190613b46906005908f908f908a8a614c19565b91509150613b568a8c8484614cbb565b8a600f0b600014613cd95760008b600f0b1215613bd1578315613b9f5760028d810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b8215613bd15760028c810b810b6000908152600560205260408120818155600181018290559182018190556003909101555b6000613be88e8e8e8b602001518c60000151613d06565b919b5099509050600f81900b15613cd75760035460408901516001600160801b0390911690600090613c3b90613c1c61390a565b60208d01516003548690600160801b90046001600160801b0316614089565b90508061ffff168a6040015161ffff1614613caa57613c67613c5b61390a565b8b60200151838561415d565b6002805461ffff60b81b1916600160b81b61ffff938416021761ffff60c81b1916600160c81b92841692909202919091179055600380546001600160801b031690555b613cb4828f614941565b600380546001600160801b0319166001600160801b039290921691909117905550505b505b505050505050509450945094915050565b8082016001600160801b0380841690821610156136a757600080fd5b60008060008760020b8560020b1215613d3b57613d34613d258961435f565b613d2e8961435f565b88614e99565b9250613d93565b8660020b8560020b1215613d7557613d5684613d2e8961435f565b9250613d6b613d648961435f565b8588614ede565b9150859050613d93565b613d90613d818961435f565b613d8a8961435f565b88614ede565b91505b955095509592505050565b60006401000276a36001600160a01b03831610801590613dda575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038316105b613e0f576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b600160201b600160c01b03602083901b166001600160801b03811160071b81811c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c97908811961790941790921717909117171760808110613ea157607f810383901c9150613eab565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d607f198f0160401b60c09190911c6001603f1b161760c19b909b1c6001603e1b169a909a1760c29990991c6001603d1b169890981760c39790971c6001603c1b169690961760c49590951c6001603b1b169490941760c59390931c6001603a1b169290921760c69190911c600160391b161760c79190911c600160381b161760c89190911c600160371b161760c99190911c600160361b161760ca9190911c600160351b161760cb9190911c600160341b161760cc9190911c600160331b161760cd9190911c600160321b1617693627a301d71055774c8581026f028f6481ab7f045a5af012a19d003aa9198101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b1461407a57886001600160a01b031661405e8261435f565b6001600160a01b031611156140735781614075565b805b61407c565b815b9998505050505050505050565b60408051630eea437960e11b815261ffff8716600482015263ffffffff86166024820152600285900b60448201526001600160801b0380851660648301528316608482015290516000916001600160a01b037f000000000000000000000000b1aad177126d9094a1c410f591132aa9dddf94051691631dd486f29160a48082019260209290919082900301818787803b15801561412557600080fd5b505af1158015614139573d6000803e3d6000fd5b505050506040513d602081101561414f57600080fd5b505190505b95945050505050565b6040805163c53a182f60e01b815263ffffffff86166004820152600285900b602482015261ffff841660448201526001600160801b038316606482015290516000916001600160a01b037f000000000000000000000000b1aad177126d9094a1c410f591132aa9dddf9405169163c53a182f91608480820192602092909190829003018186803b1580156141f057600080fd5b505afa158015614204573d6000803e3d6000fd5b505050506040513d602081101561421a57600080fd5b50516040805161ffff8316815290519192507f598b9f043c813aa6be3426ca60d1c65d17256312890be5118dab55b0775ebe2a919081900360200190a1949350505050565b6000806000603c9050808507151960008612168186050394505082156142f657600884901c600181810b900b60009081526020879052604090205460ff868116918282039091161b80156142d7576142b681614f0e565b60ff0360ff16870396506142c987614f51565b600194509450505050614357565b8160ff16870396506142e887614f51565b600094509450505050614357565b6001938401600881901c80860b90950b60009081526020879052604090205490949060ff861690811c801561434357614333818260000316614f89565b60ff16870196506142c987614f51565b8160ff0360ff16870196506142e887614f51565b935093915050565b6000600282810b60171d90818418829003900b620d89e88111156143ae576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b6000600182166143c257600160801b6143d4565b6ffffcb933bd6fad37aa2d162d1a5940015b6001600160881b0316905060028216156143fe576ffff97272373d413259a46990580e213a0260801c5b600482161561441d576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b600882161561443c576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b601082161561445b576fffcb9843d60f6159c9db58835c9266440260801c5b602082161561447a576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615614499576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156144b8576ffe5dee046a99a2a811c461f1969c30530260801c5b6101008216156144d8576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b6102008216156144f8576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615614518576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615614538576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615614558576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615614578576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615614598576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156145b8576f31be135f97d08fd981231505542fcfa60260801c5b620100008216156145d9576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b620200008216156145f9576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614618576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614635576b048a170391f7dc42444e8fa20260801c5b60008560020b131561465057806000198161464c57fe5b0490505b600160201b810615614663576001614666565b60005b60ff16602082901c019350505050919050565b6000806000806154db8a61468f5761509d614693565b6150ac5b90506000871261477f5760006146bb888861ffff16620f42400362ffffff16620f424061382a565b90506146cc8a8c8b8563ffffffff16565b94508481106146f9578995506146f28561ffff891662ffffff620f4240829003166137b5565b9250614758565b6147058b8a838f6150bb565b9550856001600160a01b03168a6001600160a01b03161461473b5761472f868c8b8563ffffffff16565b94508488039250614758565b6147558561ffff891662ffffff620f4240829003166137b5565b92505b614777868c8b8f61476b576150cb61476f565b6150da5b63ffffffff16565b93505061482f565b6154db8b61478f576150cb614793565b6150da5b90506147a48a8c8b8463ffffffff16565b93508760000397508388106147bb57899550614800565b6147c78b8a8a8f6150e9565b9550856001600160a01b03168a6001600160a01b0316146147f4576147f1868c8b8463ffffffff16565b93505b87841115614800578793505b61480f868c8b8563ffffffff16565b945061482b8561ffff891662ffffff620f4240829003166137b5565b9250505b509650965096509692505050565b6000600160ff1b821061484f57600080fd5b5090565b818101828112156000831215146136a757600080fd5b60008215806148845750508181028183828161488157fe5b04145b6136a757600080fd5b600286810b810b60009081526020899052604090206003810180546001600160a01b03600160381b63ffffffff600160d81b808504821689039091160263ffffffff60d81b199093169290921782810482168903909116909102600160381b600160d81b031990911617600681810b8703900b66ffffffffffffff1666ffffffffffffff199091161790559081018054860390556001810180548703905554600160801b9004600f0b979650505050505050565b60008082600f0b12156149a657826001600160801b03168260000384039150816001600160801b0316106149a1576040805162461bcd60e51b81526020600482015260026024820152614c5360f01b604482015290519081900360640190fd5b6136a7565b826001600160801b03168284019150816001600160801b031610156136a7576040805162461bcd60e51b81526020600482015260026024820152614c4160f01b604482015290519081900360640190fd5b600289810b900b600090815260208b9052604081208054600160801b8104600f0b906001600160801b031683614a2d828d614941565b90506e023746e6a58dcb13d4af821b93f0636001600160801b03821610614a80576040805162461bcd60e51b81526020600482015260026024820152614c4f60f01b604482015290519081900360640190fd5b85614aa357614a9e614a99600f85810b908f900b614853565b6139fe565b614ab7565b614ab7614a99600f85810b908f900b6138f4565b84546001600160801b03838116600f9390930b8116600160801b02918116919091176001600160801b03191682178655901595508216614b8057841594508c60020b8e60020b13614b6857600184018b9055600284018a9055600384018054600160381b600160d81b031916600160381b6001600160a01b038c16021766ffffffffffffff191666ffffffffffffff60068b900b161763ffffffff60d81b1916600160d81b63ffffffff8a16021790555b6003840180546001600160f81b0316600160f81b1790555b505050509a9950505050505050505050565b603c600282900b0760020b15614be4576040805162461bcd60e51b81526020600482015260126024820152711d1a58dac81a5cc81b9bdd081cdc1858d95960721b604482015290519081900360640190fd5b603c60029190910b05600881901c600190810b810b60009081526020939093526040909220805460ff9092169290921b189055565b600285810b810b60009081526020889052604080822087840b80850b84529183209293849391929088900b1215614c93578860020b8760020b12614c6e57816001015486039350816002015485039250614c7d565b81600101549350816002015492505b6001810154600282015494039390920391614cae565b81600101548160010154039350816002015481600201540392505b5050965096945050505050565b83546001600160801b03811690600160801b900463ffffffff16600f85900b614d26576000826001600160801b031611614d21576040805162461bcd60e51b815260206004820152600260248201526104e560f41b604482015290519081900360640190fd5b614ddf565b600085600f0b1215614d655760045463ffffffff168015614d63578063ffffffff1682614d5161390a565b0363ffffffff161015614d6357600080fd5b505b6000614d718387614941565b9050806000826001600160801b031611614d8c576000614da5565b600087600f0b13614d9d5782614da5565b614da561390a565b88546001600160801b039092166001600160801b031963ffffffff909216600160801b0263ffffffff60801b199093169290921716178755505b600186015460028701546000868314614e165760018901879055614e138388036001600160801b038716600160801b61382a565b90505b6000828714614e435760028a01879055614e408388036001600160801b038816600160801b61382a565b90505b6001600160801b038282171615614e8d5760038a0180546001600160801b031981166001600160801b039182168501821617808216600160801b9182900483168501909216021790555b50505050505050505050565b60008082600f0b1215614ec657614ebe614eb985858560000360006150f9565b61483d565b600003614ed6565b614ed6614eb985858560016150f9565b949350505050565b60008082600f0b1215614efe57614ebe614eb98585856000036000615188565b614ed6614eb98585856001615188565b600181811c909117600281901c17600481901c17600881901c17601081901c17602081901c17604081901c17608081901c179081901c900360006136a782614f89565b603c8102620d89e719600282900b1215614f705750620d89e719613a0f565b620d89e8600282900b1315613a0f5750620d89e8919050565b7f55555555555555555555555555555555555555555555555555555555555555558116156001600160801b0382161560071b176001600160401b03600160801b03600160c01b0382161560061b177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff82161560051b177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff82161560041b177eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff82161560031b177f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f82161560021b177f3333333333333333333333333333333333333333333333333333333333333333919091161560011b1790565b6000614ed68385846001615188565b6000614ed684848460016150f9565b60006141548585858560016151f9565b6000614ed683858460006150f9565b6000614ed68484846000615188565b60006141548585858560006151f9565b60006001600160a01b038585038116908516811061511657600080fd5b600160601b600160e01b03606085901b168361515757866001600160a01b031661514a8383896001600160a01b031661382a565b8161515157fe5b0461517d565b61517d61516e8383896001600160a01b03166137b5565b886001600160a01b03166153ed565b979650505050505050565b6000846001600160a01b0316846001600160a01b031610156151a957600080fd5b6001600160a01b0385850316826151d7576151d281856001600160801b0316600160601b61382a565b6151ef565b6151ef81856001600160801b0316600160601b6137b5565b9695505050505050565b600080866001600160a01b03161161521057600080fd5b6000856001600160801b03161161522657600080fd5b8115158315151415615318578361523e575084614154565b600160601b600160e01b03606086901b1682156152cc576001600160a01b0387168581029086828161526c57fe5b04141561529d5781810182811061529b57615291838a6001600160a01b0316836137b5565b9350505050614154565b505b6152c3826152be888b6001600160a01b031686816152b757fe5b0490613697565b6153ed565b92505050614154565b6001600160a01b038716858102908682816152e357fe5b04146152ee57600080fd5b8082116152fa57600080fd5b6152c3615313838a6001600160a01b03168486036137b5565b6153f8565b811561537f576153786153136001600160a01b038611156153505761534b86600160601b896001600160801b031661382a565b615368565b6001600160801b038716606087901b8161536657fe5b045b6001600160a01b03891690613697565b9050614154565b60006001600160a01b038511156153ad576153a885600160601b886001600160801b03166137b5565b6153c4565b6153c4606086901b6001600160801b0388166153ed565b905080876001600160a01b0316116153db57600080fd5b6001600160a01b038716039050614154565b808204910615150190565b806001600160a01b0381168114613a0f57600080fd5b604080516060810182526000808252602082018190529181019190915290565b604080516101c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052906101208201908152600060208201819052604082018190526060820181905260809091015290565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565bfefec42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67a164736f6c6343000706000a
Deployed Bytecode Sourcemap
996:34664:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;494:40:4;;;:::i;:::-;;;;-1:-1:-1;;;;;494:40:4;;;;;;;;;;;;;;19521:1531:1;;;;;;;;;;;;;;;;-1:-1:-1;;;;;19521:1531:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;19521:1531:1;;;;;;-1:-1:-1;;;19521:1531:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;19521:1531:1;;;;;;;;;;-1:-1:-1;19521:1531:1;;-1:-1:-1;19521:1531:1;-1:-1:-1;19521:1531:1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;1112:40:5;;;:::i;:::-;;;;;;;;;;;;;;;;;;;992:33;;;:::i;:::-;;;;-1:-1:-1;;;;;992:33:5;;;;;;;;;;;;;;35347:311:1;;;;;;;;;;;;;;;;-1:-1:-1;35347:311:1;;;;:::i;:::-;;309:53:4;;;:::i;32660:1920:1:-;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;32660:1920:1;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;32660:1920:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;32660:1920:1;;;;;;;;;;-1:-1:-1;32660:1920:1;;-1:-1:-1;32660:1920:1;-1:-1:-1;32660:1920:1;:::i;16257:935::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;16257:935:1;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;16257:935:1;;;;;;;-1:-1:-1;16257:935:1;;;;:::i;:::-;;;;-1:-1:-1;;;;;16257:935:1;;;;;;;;;;;;;;;;;;;;;;;;1789:54;;;;;;;;;;;;;;;;-1:-1:-1;1789:54:1;;:::i;:::-;;;;-1:-1:-1;;;;;1789:54:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;744:44:5;;;:::i;:::-;;;;;;;;;;;;;;;;812:122:4;;;:::i;21094:2120:1:-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;21094:2120:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;21094:2120:1;;;-1:-1:-1;;;21094:2120:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;21094:2120:1;;;;;;;;;;-1:-1:-1;21094:2120:1;;-1:-1:-1;21094:2120:1;-1:-1:-1;21094:2120:1;:::i;2623:398::-;;;;;;;;;;;;;;;;-1:-1:-1;2623:398:1;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2623:398:1;;;;;;;;-1:-1:-1;;;;;2623:398:1;;;;;;;-1:-1:-1;2623:398:1;;;-1:-1:-1;;;;;2623:398:1;;;;;;;;;;;;;;34634:379;;;;;;;;;;;;;;;;-1:-1:-1;34634:379:1;;;;;;;;;;;:::i;35067:226::-;;;;;;;;;;;;;;;;-1:-1:-1;35067:226:1;-1:-1:-1;;;;;35067:226:1;;:::i;3189:2093::-;;;;;;;;;;;;;;;;-1:-1:-1;3189:2093:1;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;;;;3189:2093:1;;;;;;;;;;;;;;;;;;;;;;5329:512;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5329:512:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5329:512:1;;;;;;;;;;-1:-1:-1;5329:512:1;;-1:-1:-1;5329:512:1;-1:-1:-1;5329:512:1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17234:692;;;;;;;;;;;;;;;;-1:-1:-1;17234:692:1;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;17234:692:1;;:::i;13937:2278::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;13937:2278:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;13937:2278:1;;;;;;;;;;;;-1:-1:-1;13937:2278:1;;;-1:-1:-1;;;13937:2278:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;13937:2278:1;;;;;;;;;;-1:-1:-1;13937:2278:1;;-1:-1:-1;13937:2278:1;-1:-1:-1;13937:2278:1;:::i;:::-;;;;;;;;;;;;;;-1:-1:-1;;;;;13937:2278:1;;;;;;;;;;;;;;408:41:4;;;:::i;1368:51:5:-;;;;;;;;;;;;;;;;-1:-1:-1;1368:51:5;;;;:::i;665:102:4:-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;579:40;;;:::i;912:39:5:-;;;:::i;:::-;;;;-1:-1:-1;;;;;912:39:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;912:39:5;;;;;;;;;;;;;;;;;;828:44;;;:::i;1272:56::-;;;;;;;;;;;;;;;;-1:-1:-1;1272:56:5;;;;:::i;:::-;;;;-1:-1:-1;;;;;1272:56:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1272:56:5;;;-1:-1:-1;;;;;1272:56:5;-1:-1:-1;1272:56:5;;;;;;;;;;;;;;;;;;;;;;;;5883:494:1;;;;;;;;;;;;;;;;-1:-1:-1;5883:494:1;-1:-1:-1;;;;;5883:494:1;;:::i;1192:39:5:-;;;:::i;494:40:4:-;;;:::o;19521:1531:1:-;19689:14;19705;19727:20;19753:17;19776:24;19806:20;19998:64;20020:9;20031:14;20047;19998:21;:64::i;:::-;19918:144;;-1:-1:-1;19918:144:1;;-1:-1:-1;19918:144:1;;-1:-1:-1;19918:144:1;-1:-1:-1;19918:144:1;-1:-1:-1;19918:144:1;-1:-1:-1;20069:695:1;;;;20106:1;20096:7;:11;20092:82;;;20109:65;20137:6;20145:9;20165:7;20164:8;;20109:27;:65::i;:::-;20208:22;20233:15;:13;:15::i;:::-;20208:40;;20256:37;20270:7;20279;20288:4;;20256:13;:37::i;:::-;20391:15;:13;:15::i;:::-;20351:36;:14;20378:7;20351:18;:36::i;:::-;:55;;20343:71;;;;;-1:-1:-1;;;20343:71:1;;;;;;;;;;;;-1:-1:-1;;;20343:71:1;;;;;;;;;;;;;;;20069:695;;;;20449:1;20439:7;:11;20435:82;;;20452:65;20480:6;20488:9;20508:7;20507:8;;20452:27;:65::i;:::-;20551:22;20576:15;:13;:15::i;:::-;20551:40;;20599:37;20613:7;20622;20631:4;;20599:13;:37::i;:::-;20734:15;:13;:15::i;:::-;20694:36;:14;20721:7;20694:18;:36::i;:::-;:55;;20686:71;;;;;-1:-1:-1;;;20686:71:1;;;;;;;;;;;;-1:-1:-1;;;20686:71:1;;;;;;;;;;;;;;;20069:695;;20774:16;;20770:96;;20800:59;20817:9;:27;;20838:6;20817:27;;;20829:6;20817:27;20846:12;20800:16;:59::i;:::-;20877:90;;;;;;;;;;;;-1:-1:-1;;;;;20877:90:1;;;;;;;-1:-1:-1;;;;;20877:90:1;;;;;;;;;;;;;;;;;;;;20882:10;;-1:-1:-1;;;;;;;;;;;20877:90:1;;;;;;;;;-1:-1:-1;;20973:11:1;:27;;-1:-1:-1;;;;20973:27:1;-1:-1:-1;;;20973:27:1;;;-1:-1:-1;19521:1531:1;;;;-1:-1:-1;19521:1531:1;-1:-1:-1;;;;;;19521:1531:1:o;1112:40:5:-;;;;;;:::o;992:33::-;;;-1:-1:-1;;;;;992:33:5;;:::o;35347:311:1:-;1990:7;-1:-1:-1;;;;;1974:30:1;;:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1974:32:1;-1:-1:-1;;;;;1960:46:1;:10;:46;1952:55;;;;;;610:6:22::1;35455:56:1;::::0;::::1;;::::0;::::1;::::0;:101:::1;;-1:-1:-1::0;35515:17:1::1;::::0;:41:::1;::::0;;::::1;:17:::0;::::1;:41;;35455:101;35447:110;;;::::0;::::1;;35563:17;:40:::0;;-1:-1:-1;;35563:40:1::1;;::::0;::::1;::::0;;::::1;::::0;;;35614:39:::1;::::0;;;;;;::::1;::::0;::::1;::::0;;;;;;::::1;35347:311:::0;:::o;309:53:4:-;;;:::o;32660:1920:1:-;1570:11:5;:20;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;32819:9:1::1;::::0;-1:-1:-1;;;;;32819:9:1::1;::::0;32834:28:::1;;;::::0;;-1:-1:-1;;;32834:28:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;32834:28:1;;;;;;;;;;;;;::::1;;32883:11;:15:::0;-1:-1:-1;;;32883:15:1;::::1;;;32869:11;::::0;32948:15:::1;:13;:15::i;:::-;32923:40:::0;-1:-1:-1;32973:11:1;;32969:147:::1;;33001:45;33027:7;33036:4;33001:45;;33042:3;33001:25;:45::i;:::-;32994:52;;33054:55;33082:6;33090:9;33101:7;33054:27;:55::i;:::-;33122:12;33140:22:::0;33165:15:::1;:13;:15::i;:::-;33140:40:::0;-1:-1:-1;33190:11:1;;33186:147:::1;;33218:45;33244:7;33253:4;33218:45;;33259:3;33218:25;:45::i;:::-;33211:52;;33271:55;33299:6;33307:9;33318:7;33271:27;:55::i;:::-;33339:72;::::0;-1:-1:-1;;;33339:72:1;;::::1;::::0;::::1;::::0;;;;;;;;;;;;;;;;;;;;;;33361:10:::1;::::0;33339:54:::1;::::0;33394:4;;33400;;33406;;;;33339:72;;;;33406:4;;;;33339:72;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;33418:13;33450:7;-1:-1:-1::0;;;;;33434:37:1::1;;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;33434:39:1;;-1:-1:-1;33480:13:1::1;33496:15;:13;:15::i;:::-;33480:31:::0;-1:-1:-1;33480:31:1;33525:24:::1;:14:::0;33544:4;33525:18:::1;:24::i;:::-;:33;;33517:48;;;::::0;;-1:-1:-1;;;33517:48:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;33517:48:1;;;;;;;;;;;;;::::1;;33571:23:::0;;::::1;33605:9:::0;;33601:388:::1;;33652:11;:30:::0;-1:-1:-1;;;33652:30:1;::::1;;;33624:25;33715:23:::0;;33711:182:::1;;-1:-1:-1::0;725:4:22::1;33759:27:1;::::0;::::1;::::0;::::1;33758:67;33835:49;33863:6;33871:5:::0;33758:67;33835:27:::1;:49::i;:::-;33924:58;33940:13:::0;;::::1;-1:-1:-1::0;;;;;;;;33924:58:1;::::1;:15;:58::i;:::-;33900:20;:82:::0;;;;::::1;::::0;;-1:-1:-1;;33601:388:1::1;33995:13;34011:15;:13;:15::i;:::-;33995:31:::0;-1:-1:-1;33995:31:1;34040:24:::1;:14:::0;34059:4;34040:18:::1;:24::i;:::-;:33;;34032:48;;;::::0;;-1:-1:-1;;;34032:48:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;34032:48:1;;;;;;;;;;;;;::::1;;34086:23:::0;;::::1;34120:9:::0;;34116:388:::1;;34167:11;:30:::0;-1:-1:-1;;;34167:30:1;::::1;;;34139:25;34230:23:::0;;34226:182:::1;;-1:-1:-1::0;725:4:22::1;34274:27:1;::::0;::::1;::::0;::::1;34273:67;34350:49;34378:6;34386:5:::0;34273:67;34350:27:::1;:49::i;:::-;34439:58;34455:13:::0;;::::1;-1:-1:-1::0;;;;;;;;34439:58:1;::::1;:15;:58::i;:::-;34415:20;:82:::0;;;;::::1;::::0;;-1:-1:-1;;34116:388:1::1;34533:9;-1:-1:-1::0;;;;;34515:60:1::1;34521:10;-1:-1:-1::0;;;;;34515:60:1::1;;34544:7;34553;34562:5;34569;34515:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;-1:-1:-1;;;;;;;;;;;;32660:1920:1:o;16257:935::-;1570:11:5;:20;16434:15:1;;;;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;-1:-1:-1;16502:52:1::1;16522:10;16534::::0;16546:7;16502:19:::1;:52::i;:::-;16610:14;::::0;::::1;::::0;;;-1:-1:-1;;;;;;16610:14:1;;::::1;::::0;-1:-1:-1;;;16626:14:1;::::1;::::0;::::1;::::0;16658:32;::::1;-1:-1:-1::0;;16658:67:1::1;;16709:16;16658:67;;;16693:13;16658:67;16648:77:::0;-1:-1:-1;;;;;;16741:32:1;;::::1;::::0;;::::1;;:67;;16792:16;16741:67;;;16776:13;16741:67;16731:77:::0;-1:-1:-1;16819:17:1;;::::1;-1:-1:-1::0;;;;;16819:22:1::1;::::0;16815:292:::1;;16851:14;::::0;::::1;:40:::0;;-1:-1:-1;;;;;;16851:40:1::1;16868:23:::0;;::::1;-1:-1:-1::0;;;;;16851:40:1;;::::1;::::0;;;::::1;16899::::0;::::1;-1:-1:-1::0;;;16916:23:1;;::::1;16899:40:::0;::::1;;;::::0;;;16952:11;::::1;::::0;16948:72:::1;;16965:55;16993:6;17001:9:::0;-1:-1:-1;;;;;16965:55:1;::::1;:27;:55::i;:::-;-1:-1:-1::0;;;;;17032:11:1;::::1;::::0;17028:72:::1;;17045:55;17073:6;17081:9:::0;-1:-1:-1;;;;;17045:55:1;::::1;:27;:55::i;:::-;17118:69;::::0;;-1:-1:-1;;;;;17118:69:1;::::1;::::0;;-1:-1:-1;;;;;17118:69:1;;::::1;;::::0;::::1;::::0;;::::1;::::0;;;;;;::::1;::::0;;::::1;::::0;;;;::::1;::::0;17126:10:::1;::::0;17118:69:::1;::::0;;;;;;;;::::1;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;-1:-1:-1;16257:935:1;;;;-1:-1:-1;16257:935:1;-1:-1:-1;;;;16257:935:1:o;1789:54::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1789:54:1;;;;-1:-1:-1;;;1789:54:1;;;;;;;;;;;;;;;;;;:::o;744:44:5:-;;;;:::o;812:122:4:-;520:35:22;812:122:4;:::o;21094:2120:1:-;21521:11;:20;21308:14;;;;-1:-1:-1;;;21521:20:1;;;;21513:36;;;;;-1:-1:-1;;;21513:36:1;;;;;;;;;;;;-1:-1:-1;;;21513:36:1;;;;;;;;;;;;;;;21555:11;:28;;-1:-1:-1;;;;21555:28:1;;;21589:403;;;;21612:22;21637:15;:13;:15::i;:::-;21612:40;;21660:38;21674:14;21690:1;21693:4;;21660:13;:38::i;:::-;21779:1;21739:35;21759:14;21739:15;:13;:15::i;:::-;:19;;:35::i;:::-;21715:60;;;21714:66;21706:82;;;;;-1:-1:-1;;;21706:82:1;;;;;;;;;;;;-1:-1:-1;;;21706:82:1;;;;;;;;;;;;;;;21589:403;;;;21809:22;21834:15;:13;:15::i;:::-;21809:40;;21857:38;21871:1;21874:14;21890:4;;21857:13;:38::i;:::-;21976:1;21936:35;21956:14;21936:15;:13;:15::i;:35::-;21912:60;;;21911:66;21903:82;;;;;-1:-1:-1;;;21903:82:1;;;;;;;;;;;;-1:-1:-1;;;21903:82:1;;;;;;;;;;;;;;;21589:403;;21997:11;:27;;-1:-1:-1;;;;21997:27:1;-1:-1:-1;;;21997:27:1;;;-1:-1:-1;;;;22304:64:1;22326:9;22337:14;22353;22304:21;:64::i;:::-;22224:144;;-1:-1:-1;22224:144:1;;-1:-1:-1;22224:144:1;;-1:-1:-1;22224:144:1;-1:-1:-1;22224:144:1;-1:-1:-1;22224:144:1;-1:-1:-1;22413:513:1;;;;22450:1;22440:7;:11;22436:82;;;22453:65;22481:6;22489:9;22509:7;22508:8;;22453:27;:65::i;:::-;22570:14;22560:7;:24;22556:111;;;22586:81;22614:6;22622;22638:27;:14;22657:7;22638:18;:27::i;:::-;22586;:81::i;:::-;22413:513;;;22702:1;22692:7;:11;22688:82;;;22705:65;22733:6;22741:9;22761:7;22760:8;;22705:27;:65::i;:::-;22822:14;22812:7;:24;22808:111;;;22838:81;22866:6;22874;22890:27;:14;22909:7;22890:18;:27::i;22838:81::-;22936:16;;22932:96;;22962:59;22979:9;:27;;23000:6;22979:27;;22962:59;23039:90;;;;;;;;;;;;-1:-1:-1;;;;;23039:90:1;;;;;;;-1:-1:-1;;;;;23039:90:1;;;;;;;;;;;;;;;;;;;;23044:10;;-1:-1:-1;;;;;;;;;;;23039:90:1;;;;;;;;;-1:-1:-1;;23135:11:1;:27;;-1:-1:-1;;;;23135:27:1;-1:-1:-1;;;23135:27:1;;;-1:-1:-1;21094:2120:1;;;;-1:-1:-1;21094:2120:1;-1:-1:-1;;;;;;;21094:2120:1:o;2623:398::-;2713:16;2737:21;2766:20;2794:37;2839:27;2874:17;2899:36;2978:19;-1:-1:-1;;;;;2957:52:1;;3010:5;2957:59;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2957:59:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2957:59:1;;-1:-1:-1;2957:59:1;-1:-1:-1;2957:59:1;-1:-1:-1;2957:59:1;;-1:-1:-1;2957:59:1;-1:-1:-1;2623:398:1;-1:-1:-1;;2623:398:1:o;34634:379::-;1570:11:5;:20;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;1974:32:1::1;::::0;;-1:-1:-1;;;1974:32:1;;;;1990:7:::1;-1:-1:-1::0;;;;;1974:30:1::1;::::0;-1:-1:-1;;1974:32:1::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;:30;:32;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;1974:32:1;-1:-1:-1;;;;;1960:46:1::1;:10;:46;1952:55;;;::::0;::::1;;664:3:22::2;34756:44:1;::::0;::::2;;::::0;::::2;::::0;34755:96:::2;;-1:-1:-1::0;664:3:22::2;34806:44:1;::::0;::::2;;;34755:96;34747:105;;;::::0;::::2;;34859:11;34858:97:::0;;-1:-1:-1;;;;34858:97:1::2;-1:-1:-1::0;;;;34858:97:1;;::::2;::::0;;::::2;::::0;;;::::2;-1:-1:-1::0;;;;34858:97:1::2;-1:-1:-1::0;;;34858:97:1;;::::2;::::0;;::::2;;::::0;;;34966:42:::2;::::0;;;;;::::2;::::0;::::2;::::0;;;;;;::::2;::::0;;;;;;;;;::::2;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;34634:379:1:o;35067:226::-;35179:7;-1:-1:-1;;;;;35163:39:1;;:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;35163:41:1;-1:-1:-1;;;;;35149:55:1;:10;:55;35141:64;;;;;;35211:15;:36;;-1:-1:-1;;;;;;;;35211:36:1;-1:-1:-1;;;;;;;;35211:36:1;;;;;;;;;;;;35259:29;;;;-1:-1:-1;;35259:29:1;35067:226;:::o;3189:2093::-;3346:25;;;3305:10;3317:7;2104:21;2094:31;;;;;2086:47;;;;;-1:-1:-1;;;2086:47:1;;;;;;;;;;;;-1:-1:-1;;;2086:47:1;;;;;;;;;;;;;;;2157:10;2147:20;;:7;:20;;;2139:36;;;;;-1:-1:-1;;;2139:36:1;;;;;;;;;;;;-1:-1:-1;;;2139:36:1;;;;;;;;;;;;;;;-1:-1:-1;;2189:34:1;;;;;2181:50;;;;;-1:-1:-1;;;2181:50:1;;;;;;;;;;;;-1:-1:-1;;;2181:50:1;;;;;;;;;;;;;;;3463:24:::1;;:::i;:::-;3535:17;::::0;;::::1;::::0;::::1;3501:31;3535:17:::0;;;:5:::1;:17;::::0;;;;;;;;3651:26:::1;::::0;::::1;::::0;-1:-1:-1;;;3728:24:1;::::1;;;3614:23:::0;;::::1;3560:200:::0;;;;-1:-1:-1;;;3687:31:1;::::1;-1:-1:-1::0;;;;;3687:31:1::1;3583:29:::0;;::::1;3560:200:::0;;;;3651:26:::1;::::0;;::::1;3560:200:::0;::::1;::::0;::::1;::::0;;3535:17;-1:-1:-1;;;3776:18:1;::::1;;;3768:27;;;::::0;::::1;;2237:1;3808:24;;:::i;:::-;3880:14;::::0;;::::1;::::0;::::1;3846:31;3880:14:::0;;;:5:::1;:14;::::0;;;;;;;;3993:26:::1;::::0;::::1;::::0;-1:-1:-1;;;4070:24:1;::::1;;;3956:23:::0;;::::1;3902:200:::0;;;;-1:-1:-1;;;4029:31:1;::::1;-1:-1:-1::0;;;;;4029:31:1::1;3925:29:::0;;::::1;3902:200:::0;;;;3993:26:::1;::::0;;::::1;3902:200:::0;::::1;::::0;::::1;::::0;;3880:14;-1:-1:-1;;;4119:18:1;::::1;;;4111:27;;;::::0;::::1;;-1:-1:-1::0;4204:11:1::1;:16:::0;;-1:-1:-1;;;4204:16:1;::::1;::::0;::::1;::::0;-1:-1:-1;;;4222:26:1;;::::1;;;::::0;4260:24;;::::1;::::0;;;::::1;;4256:243;;;4334:5;:20;;;4311:5;:20;;;:43;4396:5;:29;;;4364:5;:29;;;:61;4461:5;:23;;;4435:5;:23;;;:49;4294:198;;;;;;;;;;;;4256:243;4523:7;4509:21;;:11;:21;;;4505:576;;;4540:17;4560;:15;:17::i;:::-;4777:9;::::0;4540:37;;-1:-1:-1;4586:26:1::1;::::0;;;4665:129:::1;::::0;4540:37;;4586:26;;4725:11;;4746:21;;-1:-1:-1;;;;;4777:9:1::1;4665:19;:129::i;:::-;4585:209;;;;;;4865:5;:20;;;4842:5;:20;;;4819;:43;:66;4965:5;:29;;;4933:5;:29;;;4895:35;:67;:99;5043:5;:23;;;5017:5;:23;;;5004:10;:36;:62;4802:272;;;;;;;;;;;;;;;4505:576;5125:5;:20;;;5102:5;:20;;;:43;5185:5;:29;;;5153:5;:29;;;:61;5248:5;:23;;;5222:5;:23;;;:49;5087:190;;;;;;;;;;2237:1;3189:2093:::0;;;;;;;:::o;5329:512::-;5438:30;;;;-1:-1:-1;;;;;5665:19:1;5644:55;;5709:17;:15;:17::i;:::-;5757:11;:16;;5819:9;;5644:192;;;;;;-1:-1:-1;;;;;;5644:192:1;;;;;;;;;;;;-1:-1:-1;;;5757:16:1;;;;5644:192;;;;;;;;-1:-1:-1;;;5783:26:1;;;;;5644:192;;;;;;-1:-1:-1;;;;;5819:9:1;;;5644:192;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;5736:11;;5644:192;;5757:16;;5783:26;5644:192;;;;;5736:11;-1:-1:-1;5644:192:1;;;5736:11;5644:192;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5644:192:1;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;-1:-1:-1;5644:192:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5631:205;;;;;;;;5329:512;;;;;;;:::o;17234:692::-;1570:11:5;:20;17381:15:1;;;;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;17351:10:1;;17363:7;;2104:21;2094:31;;;::::1;;2086:47;;;::::0;;-1:-1:-1;;;2086:47:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2086:47:1;;;;;;;;;;;;;::::1;;2157:10;2147:20;;:7;:20;;;2139:36;;;::::0;;-1:-1:-1;;;2139:36:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2139:36:1;;;;;;;;;;;;;::::1;;-1:-1:-1::0;;2189:34:1::1;::::0;;::::1;;2181:50;;;::::0;;-1:-1:-1;;;2181:50:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2181:50:1;;;;;;;;;;;;;::::1;;17422:25:::2;::::0;;17489:118:::2;17524:10;17542::::0;17560:7;17576:25:::2;-1:-1:-1::0;;;;;17576:14:1;::::2;:23;:25::i;:::-;17575:26;;17489:27;:118::i;:::-;17421:186;;;;;;17633:10;17632:11;;17614:30;;17669:10;17668:11;;17650:30;;17701:7;17691;:17;17712:1;17691:22;17687:160;;17759:14;::::0;::::2;::::0;:39:::2;::::0;-1:-1:-1;;;;;17759:14:1::2;17789:7:::0;17759:21:::2;:39::i;:::-;17800:14;::::0;::::2;::::0;:39:::2;::::0;-1:-1:-1;;;17800:14:1;::::2;-1:-1:-1::0;;;;;17800:14:1::2;17830:7:::0;17800:21:::2;:39::i;:::-;17724:14;::::0;::::2;17723:117:::0;;-1:-1:-1;;;;;17723:117:1;;::::2;-1:-1:-1::0;;;17723:117:1;;::::2;::::0;;;::::2;::::0;::::2;::::0;;;::::2;-1:-1:-1::0;;;;;;17723:117:1::2;;::::0;;17687:160:::2;17858:63;::::0;;-1:-1:-1;;;;;17858:63:1;::::2;::::0;;::::2;::::0;::::2;::::0;;;;;;;;;;;::::2;::::0;;::::2;::::0;;;;::::2;::::0;17863:10:::2;::::0;17858:63:::2;::::0;;;;;;;;::::2;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;-1:-1:-1;17234:692:1;;;;-1:-1:-1;17234:692:1;;-1:-1:-1;;;;17234:692:1:o;13937:2278::-;1570:11:5;:20;14189:15:1;;;;;;-1:-1:-1;;;1570:20:5;;;;1562:36;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;-1:-1:-1;;;1562:36:5;;;;;;;;;;;;;;;1604:11;:28;;-1:-1:-1;;;;1604:28:5;;;14148:10:1;;14160:7;;2104:21;2094:31;;;::::1;;2086:47;;;::::0;;-1:-1:-1;;;2086:47:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2086:47:1;;;;;;;;;;;;;::::1;;2157:10;2147:20;;:7;:20;;;2139:36;;;::::0;;-1:-1:-1;;;2139:36:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2139:36:1;;;;;;;;;;;;;::::1;;-1:-1:-1::0;;2189:34:1::1;::::0;;::::1;;2181:50;;;::::0;;-1:-1:-1;;;2181:50:1;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2181:50:1;;;;;;;;;;;;;::::1;;-1:-1:-1::0;;;;;14281:20:1;::::2;14273:35;;;::::0;;-1:-1:-1;;;14273:35:1;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;14273:35:1;;;;;;;;;;;;;::::2;;14323:17;::::0;14365:166:::2;14398:10:::0;14418:7;14435:35:::2;-1:-1:-1::0;;;;;14435:24:1;::::2;:33;:35::i;:::-;14480:11;:16:::0;;-1:-1:-1;;;14480:16:1;::::2;::::0;;::::2;::::0;-1:-1:-1;;;;;14506:17:1::2;14365:23;:166::i;:::-;-1:-1:-1::0;14322:209:1;;-1:-1:-1;14322:209:1;-1:-1:-1;14619:23:1::2;::::0;-1:-1:-1;14619:23:1;;-1:-1:-1;14689:11:1;;14685:50:::2;;14720:15;:13;:15::i;:::-;14702:33;;14685:50;14747:11:::0;;14743:50:::2;;14778:15;:13;:15::i;:::-;14760:33;;14743:50;14801:76;::::0;-1:-1:-1;;;14801:76:1;;::::2;::::0;::::2;::::0;;;;;;;;;;;;;;;;;;;;;;14822:10:::2;::::0;14801:52:::2;::::0;14854:7;;14863;;14872:4;;;;14801:76;;;;14872:4;;;;14801:76;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;14899:1;14889:7;:11;14885:91;;;14966:1;14947:15;14929;:13;:15::i;:::-;:33;14911:51;;;14910:57;14902:74;;;::::0;;-1:-1:-1;;;14902:74:1;;::::2;;::::0;;::::2;::::0;;;;;;;;-1:-1:-1;;;14902:74:1;;;;;;;;;;;;;::::2;;14988:11:::0;;14984:91:::2;;15065:1;15046:15;15028;:13;:15::i;:::-;:33;15010:51;;;15009:57;15001:74;;;::::0;;-1:-1:-1;;;15001:74:1;;::::2;;::::0;;::::2;::::0;;;;;;;;-1:-1:-1;;;15001:74:1;;;;;;;;;;;;;::::2;;15106:16;15088:34;;15150:7;15132:15;:25;15128:140;;;15193:67;-1:-1:-1::0;;;;;15209:24:1;::::2;15235:15:::0;15252:7;15193:15:::2;:67::i;:::-;15167:94;;15128:140;15295:7;15277:15;:25;15273:246;;;15312:23;15346:67;-1:-1:-1::0;;;;;15362:24:1;::::2;15388:15:::0;15405:7;15346:15:::2;:67::i;:::-;15312:102:::0;-1:-1:-1;;;;;;15426:33:1;;::::2;::::0;;::::2;;15422:91;;;15489:15;15471:33;;15422:91;15273:246;;-1:-1:-1::0;;;;;15533:19:1;::::2;15525:36;;;::::0;;-1:-1:-1;;;15525:36:1;;::::2;;::::0;;::::2;::::0;;;;;;;;-1:-1:-1;;;15525:36:1;;;;;;;;;;;;;::::2;;15579:17;::::0;15619:95:::2;15647:9:::0;15658:10;15670:7;15679:34:::2;-1:-1:-1::0;;;;;15679:23:1;::::2;:32;:34::i;:::-;15619:27;:95::i;:::-;15576:138;;;;;15766:15;15750:10;15732:29;;;15731:50;;15723:68;;;::::0;;-1:-1:-1;;;15723:68:1;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;15723:68:1;;;;;;;;;;;;;::::2;;15842:15;15826:10;15808:29;;;15807:50;;15799:68;;;::::0;;-1:-1:-1;;;15799:68:1;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;15799:68:1;;;;;;;;;;;;;::::2;;2237:1;;15902:7;15884:15;:25;15880:116;;;15919:70;15947:6;15955;15981:7;15963:15;:25;15919:27;:70::i;:::-;16023:7;16005:15;:25;16001:116;;;16040:70;16068:6;16076;16102:7;16084:15;:25;16040:27;:70::i;:::-;16167:7;16127:83;;16155:10;16127:83;;16144:9;-1:-1:-1::0;;;;;16127:83:1::2;;16132:10;16176:15;16193:7;16202;16127:83;;;;-1:-1:-1::0;;;;;16127:83:1::2;;;;;;-1:-1:-1::0;;;;;16127:83:1::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;1645:11:5;:27;;-1:-1:-1;;;;1645:27:5;-1:-1:-1;;;1645:27:5;;;-1:-1:-1;13937:2278:1;;;;-1:-1:-1;13937:2278:1;-1:-1:-1;13937:2278:1;-1:-1:-1;;;;;;13937:2278:1:o;408:41:4:-;;;:::o;1368:51:5:-;;;;;;;;;;;;;:::o;665:102:4:-;401:2:22;665:102:4;:::o;579:40::-;;;:::o;912:39:5:-;;;;-1:-1:-1;;;;;912:39:5;;;-1:-1:-1;;;912:39:5;;;;;;-1:-1:-1;;;912:39:5;;;;;-1:-1:-1;;;912:39:5;;;;;;;-1:-1:-1;;;912:39:5;;;;;-1:-1:-1;;;912:39:5;;;;;-1:-1:-1;;;912:39:5;;;;;:::o;828:44::-;;;;:::o;1272:56::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1272:56:5;;;-1:-1:-1;;;1272:56:5;;;;;;;;;;;;-1:-1:-1;;;1272:56:5;;-1:-1:-1;;;;;1272:56:5;;-1:-1:-1;;;1272:56:5;;;;;-1:-1:-1;;;1272:56:5;;;;;:::o;5883:494:1:-;5957:11;:17;-1:-1:-1;;;;;5957:17:1;:22;5949:37;;;;;-1:-1:-1;;;5949:37:1;;;;;;;;;;;;-1:-1:-1;;;5949:37:1;;;;;;;;;;;;;;;6057:10;6070:41;6098:12;6070:27;:41::i;:::-;6057:54;;6118:16;6137:17;:15;:17::i;:::-;6160:69;;;-1:-1:-1;;;6160:69:1;;;;;;;;;;;;;;;;;;;6118:36;;-1:-1:-1;;;;;;6181:19:1;6160:52;;;;:69;;;;;-1:-1:-1;;6160:69:1;;;;;;;;-1:-1:-1;6160:52:1;:69;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;6236:11:1;:32;;6307:23;;;;;;-1:-1:-1;;;6307:23:1;-1:-1:-1;;;;;;;;;;;;;6236:32:1;;-1:-1:-1;;;;;;6236:32:1;;;;;6274:27;-1:-1:-1;;;6274:27:1;6307:23;;;;;6342:30;;;;;;;;;;;;;;;;;-1:-1:-1;6342:30:1;;;;;;;;-1:-1:-1;6342:30:1;5883:494;;;:::o;1192:39:5:-;;;-1:-1:-1;;;1192:39:5;;-1:-1:-1;;;;;1192:39:5;;:::o;24965:7653:1:-;25107:14;25129;25151:20;25179:17;25204:24;25236:26;25277:21;25304:33;;:::i;:::-;25402:11;:17;;25477:15;-1:-1:-1;;;25477:15:1;;;;25465:9;;;:27;-1:-1:-1;;;25523:26:1;;;25500:20;;;:49;-1:-1:-1;;;;25738:28:1;;;;-1:-1:-1;;;;;25402:17:1;;;-1:-1:-1;;;;25441:16:1;;;;;;-1:-1:-1;;;;;25587:30:1;;;;;-1:-1:-1;;;25655:30:1;;;;;-1:-1:-1;;;25709:20:1;;;;;25820:24;;;;;-1:-1:-1;;;25820:24:1;;;;;;;;;;;;-1:-1:-1;;;25820:24:1;;;;;;;;;;;;;;;25861:19;25853:34;;;;;-1:-1:-1;;;25853:34:1;;;;;;;;;;;;-1:-1:-1;;;25853:34:1;;;;;;;;;;;;;;;25979:1;25962:18;;25925:16;;;25895:86;25896:27;;;25895:86;;;26045:9;;-1:-1:-1;;;;;;;;26056:25:1;;;;26009:31;;;25990:92;26045:9;;-1:-1:-1;26091:443:1;;;;-1:-1:-1;;;;;26124:29:1;;;;;;;:73;;;;-1:-1:-1;938:10:31;-1:-1:-1;;;;;26157:40:1;;;26124:73;26116:89;;;;;-1:-1:-1;;;26116:89:1;;;;;;;;;;;;-1:-1:-1;;;26116:89:1;;;;;;;;;;;;;;;26238:20;;26215;;;:43;26268:40;;;26091:443;;;-1:-1:-1;;;;;26341:29:1;;;;;;;:73;;;;-1:-1:-1;1114:49:31;-1:-1:-1;;;;;26374:40:1;;;26341:73;26333:89;;;;;-1:-1:-1;;;26333:89:1;;;;;;;;;;;;-1:-1:-1;;;26333:89:1;;;;;;;;;;;;;;;26455:20;;26432;;;:43;26485:40;;;26091:443;26542:29;;;;;;:15;;;:29;26597:17;:15;:17::i;:::-;26627:15;;26580:34;;-1:-1:-1;;;;26627:15:1;;-1:-1:-1;;;;;26627:15:1;:29;26623:557;;26725:15;;;26705:71;;;-1:-1:-1;;;26705:71:1;;;;;;;;;;;;;-1:-1:-1;;;;;26725:15:1;;;-1:-1:-1;;;;;26725:15:1;;26705:55;;:71;;;;;;;;;;;;;;-1:-1:-1;26725:15:1;26705:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;26705:71:1;;-1:-1:-1;26801:36:1;26790:7;:47;;;;;;;;;26786:386;;;26851:15;:28;;-1:-1:-1;;;;;;;;26851:28:1;;;26786:386;;;26911:33;26900:7;:44;;;;;;;;;26896:276;;;26982:33;26958:21;;;:57;26896:276;;;27047:38;27036:7;:49;;;;;;;;;27032:140;;;27123:38;27099:21;;;:62;27032:140;26623:557;;27188:24;27215:169;27240:5;:20;;;27270:14;27294:5;:15;;;27319:16;27345:5;:31;;;27215:15;:169::i;:::-;27188:196;;27478:5;:20;;;27457:41;;:17;:41;;;27453:249;;27510:40;;;:20;;;:40;27594:1;27560:31;;;:35;27617:76;27628:14;27644:11;27533:17;27676:16;27617:10;:76::i;:::-;27605:88;;:9;;;:88;27453:249;24965:7653;;;;27714:30;;:::i;:::-;-1:-1:-1;;;;;27859:33:1;;;;27937:54;:9;27968:11;27981:9;27937:30;:54::i;:::-;27901:90;;27917:16;;;27901:90;;;;;;;27902:13;;;27901:90;;;28021:42;;:27;:42::i;:::-;-1:-1:-1;;;;;28000:63:1;;;:18;;;:63;;;28246:326;;28296:9;;28315:12;;28352:35;;-1:-1:-1;28338:50:1;;;;28337:158;;28477:4;:18;;;28337:158;;;28450:14;28337:158;28505:16;28531:14;28555:5;:9;;;28246:40;:326::i;:::-;28228:14;;;28188:384;28215:11;;;28188:384;28203:10;;;28188:384;28585:16;;;;28188:384;;-1:-1:-1;28581:517:1;;;28631:40;28645:4;:14;;;28632:4;:10;;;:27;28631:38;:40::i;:::-;28613:58;;;;28741:50;28768:22;:4;:11;;;:20;:22::i;:::-;28741;;;;;:26;:50::i;:::-;28716:22;;;:75;28581:517;;;28871:22;:4;:11;;;:20;:22::i;:::-;28853:40;;;;28985:68;29012:40;29026:4;:14;;;29013:4;:10;;;:27;29012:38;:40::i;:::-;28985:22;;;;;:26;:68::i;:::-;28960:22;;;:93;28581:517;29110:18;;:22;29106:211;;29180:18;;29161:14;;;;29144:13;;725:4:22;;29161:38:1;;:18;:38::i;:::-;29160:78;;;;;29248:14;;;:23;;29160:78;;;;29248:23;;;;;;29281:27;;;;;-1:-1:-1;29106:211:1;-1:-1:-1;;;;;29329:20:1;;;29325:115;;29391:14;;;;29375:65;;-1:-1:-1;;;;;;;;29375:65:1;;:15;:65::i;:::-;29351:20;;;:89;;;;;;;29325:115;29469:18;;;;-1:-1:-1;;;;;29453:34:1;;;;;;29449:2137;;;29574:4;:16;;;29570:1664;;;29690:5;:29;;;29685:521;;29801:249;29836:14;29866:1;29883:5;:15;;;29914:5;:20;;;29950:16;29801:19;:249::i;:::-;-1:-1:-1;;;;;;;29735:315:1;29758:35;;;29735:315;;;;;;;29736:20;;;29735:315;-1:-1:-1;30064:29:1;;;:36;30138:9;:55;;30173:20;;30138:55;;;30150:20;;30138:55;30114:21;;;:79;29685:521;30321:36;30296:5;:21;;;:61;;;;;;;;;30292:162;;30393:15;;;30416:13;;;;30373:68;;;-1:-1:-1;;;30373:68:1;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;30393:15:1;;;-1:-1:-1;;;;;30393:15:1;;30373:42;;:68;;;;;-1:-1:-1;;30373:68:1;;;;;;;-1:-1:-1;30393:15:1;30373:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30292:162;30465:21;30502:9;30498:638;;;30545:264;30572:4;:13;;;30601:5;:20;;;30647:5;:21;;;30694:5;:35;;;30745:5;:20;;;30781:14;30545:5;:11;;:264;;;;;;;;;:::i;:::-;30544:265;;30527:282;;30498:638;;;30859:264;30886:4;:13;;;30915:5;:21;;;30962:5;:20;;;31008:5;:35;;;31059:5;:20;;;31095:14;30859:5;:11;;:264;;;;;;;;;:::i;:::-;30842:281;;30498:638;31167:56;31190:16;31208:14;31167:22;:56::i;:::-;31148:75;;29570:1664;;31258:9;:45;;31290:4;:13;;;31258:45;;;31286:1;31270:4;:13;;;:17;31258:45;31244:59;;29449:2137;;;31338:18;;-1:-1:-1;;;;;31322:34:1;;;;;;31318:268;;31448:41;31476:12;31448:27;:41::i;:::-;31434:55;;31499:5;;31318:268;31628:19;;;:53;;-1:-1:-1;;;;;;31651:30:1;;;;;;;31628:53;31624:83;;;31693:5;;31624:83;27838:3875;;;31753:5;:16;;;31740:29;;:9;:29;;;:353;;32024:5;:22;;;32078:14;32048:5;:27;;;:44;31740:353;;;31894:14;31864:5;:27;;;:44;31910:5;:22;;;31740:353;32213:9;;;;32224:20;;;;32101:11;32100:145;;-1:-1:-1;;;;32100:145:1;-1:-1:-1;;;;32100:145:1;;;;;-1:-1:-1;;;;32100:145:1;-1:-1:-1;;;32100:145:1;;;;;;;;;;;;-1:-1:-1;;;;32100:145:1;-1:-1:-1;;;;32100:145:1;;;;;;-1:-1:-1;;;;;;32100:145:1;-1:-1:-1;;;;;32100:145:1;;;;;;;;;;32359:105;;;-1:-1:-1;;;32359:105:1;;-1:-1:-1;;;;;32359:105:1;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32359:105:1;;-1:-1:-1;32359:105:1;;32380:19;32359:69;;;;;;:105;;;;;;;;;;;;;;:69;:105;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32359:105:1;;32325:31;;;32253:9;32252:218;;-1:-1:-1;;;;;32252:218:1;;;-1:-1:-1;;;32325:139:1;;;;32252:218;;;;;;;;;;;;;;-1:-1:-1;;;;;;32252:218:1;;;;32477:137;;;;32523:20;;;;32500;:43;32477:137;;;32587:20;;;;32564;:43;32477:137;24965:7653;;;;;;;;;;;;;:::o;723:292:34:-;865:66;;;-1:-1:-1;;;;;865:66:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;865:66:34;-1:-1:-1;;;865:66:34;;;854:78;;;;-1:-1:-1;;;;854:10:34;;;;865:66;854:78;;;865:66;854:78;;865:66;854:78;;;;;;;;;;-1:-1:-1;;854:78:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;818:114;;;;946:7;:57;;;;-1:-1:-1;958:11:34;;:16;;:44;;;989:4;978:24;;;;;;;;;;;;;;;-1:-1:-1;978:24:34;958:44;938:72;;;;;-1:-1:-1;;;938:72:34;;;;;;;;;;;;-1:-1:-1;;;938:72:34;;;;;;;;;;;;;;;723:292;;;;;:::o;2339:120:1:-;2408:46;;;-1:-1:-1;;;2408:46:1;;2448:4;2408:46;;;;;;2386:7;;-1:-1:-1;;;;;2422:6:1;2408:31;;;;:46;;;;;;;;;;;;;;;:31;:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2408:46:1;;-1:-1:-1;2339:120:1;:::o;19292:187::-;19398:76;;-1:-1:-1;;;19398:76:1;;;;;;;;;;;;;;;;;;;;;;;;;;;19419:10;;19398:52;;19451:7;;19460;;19469:4;;;;19398:76;;;;19469:4;;;;19398:76;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19292:187;;;;:::o;558:105:26:-;646:5;;;641:16;;;;633:25;;;;;;558:105;;;;:::o;2463:120:1:-;2532:46;;;-1:-1:-1;;;2532:46:1;;2572:4;2532:46;;;;;;2510:7;;-1:-1:-1;;;;;2546:6:1;2532:31;;;;:46;;;;;;;;;;;;;;;:31;:46;;;;;;;;;;18253:186;18324:13;18356:7;-1:-1:-1;;;;;18340:37:1;;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;18340:39:1;;-1:-1:-1;18385:49:1;18413:5;18340:39;18427:6;18385:27;:49::i;:::-;18253:186;;;:::o;4356:491:24:-;4464:14;4490:6;;;:37;;-1:-1:-1;;4511:5:24;;;4525:1;4520;4511:5;4520:1;4501:20;;;;;:25;4490:37;4486:357;;;4559:1;4545:11;:15;4537:24;;;;;;4602;;;4631;;;;4628:31;;4598:62;4578:90;;;4697:25;4704:1;4707;4710:11;4697:6;:25::i;:::-;4688:34;;4762:1;4747:11;4734:25;;;;;4744:1;4741;4734:25;:29;4730:107;;;-1:-1:-1;;4783:26:24;;4775:35;;;;;;4820:8;;4730:107;4356:491;;;;;:::o;757:3329::-;855:14;1176:5;;;855:14;-1:-1:-1;;1176:5:24;;1322:20;1385:5;1381:2;1378:13;1370:5;1366:2;1362:14;1358:34;1349:43;;;1513:5;1499:11;:19;1491:28;;;;;;1584:10;1580:111;;-1:-1:-1;1633:23:24;;;;-1:-1:-1;1671:13:24;;1580:111;2044:11;2041:1;2038;2031:25;2280:12;2295;;;:26;;2401:22;;;;;3128:1;3109;:15;;3108:21;;3339:17;;;3335:21;;3328:28;3393:17;;;3389:21;;3382:28;3448:17;;;3444:21;;3437:28;3503:17;;;3499:21;;3492:28;3558:17;;;3554:21;;3547:28;3614:17;;;3610:21;;;3603:28;2729:12;;;;2725:23;;;2750:1;2721:31;2083:20;;;2072:32;;;2772:12;;;;2120:21;;;;2511:16;;;;2763:21;4051:11;;-1:-1:-1;757:3329:24;;;;;:::o;13596:299:1:-;13848:8;13835:22;;;13806:25;;13794:2;13790:14;;;13787:45;13779:54;;;13776:82;13711:16;13876:14;;;:9;:14;;;;;;13596:299::o;817:105:26:-;905:5;;;900:16;;;;892:25;;;;;1632:114;1717:5;;;1712:16;;;;1688:8;1733:6;;;1712:28;1704:37;;;;;1793:132:5;1879:15;1793:132;:::o;18792:496:1:-;19152:131;;;-1:-1:-1;;;19152:131:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;19152:131:1;;;;;;;;-1:-1:-1;;;;;;;;19173:19:1;-1:-1:-1;;;;;19152:60:1;;-1:-1:-1;;19152:131:1;;;;;-1:-1:-1;;19152:131:1;;;;;;;:60;:131;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;19152:131:1;;;;;;;;;;;;;;;;;;;-1:-1:-1;19152:131:1;;-1:-1:-1;19152:131:1;-1:-1:-1;18792:496:1;-1:-1:-1;;;;;;18792:496:1:o;763:101:28:-;857:1;838:20;;;;;;830:29;;;;;;763:101;;;:::o;9237:2953:1:-;9516:84;;;;;;;;9536:11;:17;;-1:-1:-1;;;;;9536:17:1;;9516:84;;-1:-1:-1;;;9555:16:1;;;;9516:84;;;;;;;-1:-1:-1;;;9573:26:1;;;;9516:84;;;;;;;-1:-1:-1;;;;;;9618:47:1;9638:5;9645:10;9657:7;9618:19;:47::i;:::-;9673:29;9738:20;;9760;;9607:58;;-1:-1:-1;9738:20:1;9760;9673:29;9837:19;;;;;9833:974;;9866:11;9880:17;:15;:17::i;:::-;10002:10;;;;10014:20;;;;10036:9;;9866:31;;-1:-1:-1;9906:20:1;;;;9973:73;;9866:31;;9906:20;;10002:10;10014:20;-1:-1:-1;;;;;10036:9:1;9973:19;:73::i;:::-;9905:141;;;;;;10068:271;10092:10;10114:5;:10;;;10136:14;10162:21;10195;10228:29;10269:14;10295:4;10311:5;10068;:12;;:271;;;;;;;;;;;;:::i;:::-;10055:374;;;10374:4;;-1:-1:-1;10388:32:1;:9;10409:10;10388:20;:32::i;:::-;10493:10;;;;10450:267;;:5;;10474:7;;10515:14;10541:21;10574;10607:29;10648:14;10674:4;10690;10450:12;:267::i;:::-;10437:364;;;10749:4;;-1:-1:-1;10763:29:1;:9;10784:7;10763:20;:29::i;:::-;9833:974;;;;10940:10;;;;10814:28;;;;10876:138;;:5;;10907:10;;10925:7;;10958:21;10987;10876:23;:138::i;:::-;10813:201;;;;11021:90;11042:8;11052:14;11068:20;11090;11021;:90::i;:::-;11122:14;:19;;11140:1;11122:19;11118:1068;;11305:1;11288:14;:18;;;11284:133;;;11322:13;11318:43;;;11344:17;;;;;;;;;;:5;:17;;;;;11337:24;;;;;;;;;;;;;;;;;;;;11318:43;11375:10;11371:37;;;11394:14;;;;;;;;;;:5;:14;;;;;11387:21;;;;;;;;;;;;;;;;;;;;11371:37;11425:27;11503:85;11527:10;11539:7;11548:14;11564:5;:10;;;11576:5;:11;;;11503:23;:85::i;:::-;11460:128;;-1:-1:-1;11460:128:1;-1:-1:-1;11460:128:1;-1:-1:-1;11600:25:1;;;;;11596:584;;11663:9;;11725:20;;;;-1:-1:-1;;;;;11663:9:1;;;;11637:23;;11709:112;;11747:17;:15;:17::i;:::-;11766:10;;;;11795:25;;11778:15;;-1:-1:-1;;;11795:25:1;;-1:-1:-1;;;;;11795:25:1;11709:15;:112::i;:::-;11682:139;;11859:17;11835:41;;:5;:20;;;:41;;;11831:264;;11908:77;11919:17;:15;:17::i;:::-;11938:5;:10;;;11950:17;11969:15;11908:10;:77::i;:::-;11890:11;:95;;-1:-1:-1;;;;11890:95:1;-1:-1:-1;;;;11890:95:1;;;;;-1:-1:-1;;;;11997:46:1;-1:-1:-1;;;11997:46:1;;;;;;;;;;;;;12055:25;:29;;-1:-1:-1;;;;;12055:29:1;;;11831:264;12116:55;12139:15;12156:14;12116:22;:55::i;:::-;12104:9;:67;;-1:-1:-1;;;;;;12104:67:1;-1:-1:-1;;;;;12104:67:1;;;;;;;;;;-1:-1:-1;;11596:584:1;11118:1068;;9237:2953;;;;;;;;;;;;;;;:::o;1901:108:26:-;1992:5;;;-1:-1:-1;;;;;1987:16:26;;;;;;;;1979:25;;;;;12194:1142:1;12389:14;12411;12433:27;12593:10;12579:24;;:11;:24;;;12575:757;;;12623:124;12653:39;12681:10;12653:27;:39::i;:::-;12694:36;12722:7;12694:27;:36::i;:::-;12732:14;12623:29;:124::i;:::-;12613:134;;12575:757;;;12778:7;12764:21;;:11;:21;;;12760:572;;;12805:97;12835:12;12849:36;12877:7;12849:27;:36::i;12805:97::-;12795:107;;12920:100;12950:39;12978:10;12950:27;:39::i;:::-;12991:12;13005:14;12920:29;:100::i;:::-;12910:110;;13052:14;13029:37;;12760:572;;;13201:124;13231:39;13259:10;13231:27;:39::i;:::-;13272:36;13300:7;13272:27;:36::i;:::-;13310:14;13201:29;:124::i;:::-;13191:134;;12760:572;12194:1142;;;;;;;;;:::o;4330:3552:31:-;4396:10;938;-1:-1:-1;;;;;4517:23:31;;;;;;:49;;-1:-1:-1;1114:49:31;-1:-1:-1;;;;;4544:22:31;;;4517:49;4509:63;;;;;-1:-1:-1;;;4509:63:31;;;;;;;;;;;;-1:-1:-1;;;4509:63:31;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;4594:20:31;;;;-1:-1:-1;;;;;4699:41:31;;4696:1;4692:49;4777:9;;;-1:-1:-1;;;;;4830:25:31;;4827:1;4823:33;4892:9;;;4951:10;4945:17;;4942:1;4938:25;4999:9;;;5058:6;5052:13;;5049:1;5045:21;5102:9;;;5161:4;5155:11;;5152:1;5148:19;;;5203:9;;;5262:3;5256:10;;5253:1;5249:18;5303:9;;;5356:10;;;-1:-1:-1;5349:18:31;;;5403:9;;;;5449:10;;;4870;;4977;;;5080;;;5181;5281;;;5381;5473;-1:-1:-1;5499:10:31;;5495:75;;5531:3;5525;:9;5515:5;:20;;5511:24;;5495:75;;;5566:3;5560;:9;5550:5;:20;;5546:24;;5495:75;5655:9;;;5650:3;5646:19;;;5681:11;;;;5741:9;;;;5792;;5783:19;;;5818:11;;;5878:9;5929;;5920:19;;;5955:11;;;6015:9;6066;;6057:19;;;6092:11;;;6152:9;6203;;6194:19;;;6229:11;;;6289:9;6340;;6331:19;;;6366:11;;;6426:9;6477;;6468:19;;;6503:11;;;6563:9;6614;;6605:19;;;6640:11;;;6700:9;6751;;6742:19;;;6777:11;;;6837:9;6888;;6879:19;;;6914:11;;;6974:9;7025;;7016:19;;;7051:11;;;7111:9;7162;;7153:19;;;7188:11;;;7248:9;7299;;7290:19;;;7325:11;;;;7385:9;;;;7436;;7427:19;;;;;5655:9;-1:-1:-1;;5593:17:31;;5615:2;5592:25;5718:10;;;;;-1:-1:-1;;;5718:10:31;5708:21;5855:10;;;;;-1:-1:-1;;;5855:10:31;5845:21;;;;5992:10;;;;;-1:-1:-1;;;5992:10:31;5982:21;;;;6129:10;;;;;-1:-1:-1;;;6129:10:31;6119:21;;;;6266:10;;;;;-1:-1:-1;;;6266:10:31;6256:21;;;;6403:10;;;;;-1:-1:-1;;;6403:10:31;6393:21;;;;6540:10;;;;;-1:-1:-1;;;6540:10:31;6530:21;6677:10;;;;;-1:-1:-1;;;6677:10:31;6667:21;6814:10;;;;;-1:-1:-1;;;6814:10:31;6804:21;6951:10;;;;;-1:-1:-1;;;6951:10:31;6941:21;7088:10;;;;;-1:-1:-1;;;7088:10:31;7078:21;7225:10;;;;;-1:-1:-1;;;7225:10:31;7215:21;7362:10;;;;;-1:-1:-1;;;7362:10:31;7352:21;7499:10;;;;;-1:-1:-1;;;7499:10:31;7489:21;7553:24;7545:32;;-1:-1:-1;;7625:53:31;;5607:3;7624:62;;;;7731:39;7715:55;;7714:64;;7793:17;;;;;;;;;:84;;-1:-1:-1;;;;;7823:35:31;;:26;7842:6;7823:18;:26::i;:::-;-1:-1:-1;;;;;7823:35:31;;;:54;;7870:7;7823:54;;;7861:6;7823:54;7793:84;;;7813:7;7793:84;7786:91;4330:3552;-1:-1:-1;;;;;;;;;4330:3552:31:o;18443:345:1:-;18660:123;;;-1:-1:-1;;;18660:123:1;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;18660:123:1;;;;;;;;;;;;;;;-1:-1:-1;;18681:19:1;-1:-1:-1;;;;;18660:47:1;;;;:123;;;;;;;;;;;;;;;-1:-1:-1;18660:47:1;:123;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;18660:123:1;;-1:-1:-1;18443:345:1;;;;;;;;:::o;17991:258::-;18140:82;;;-1:-1:-1;;;18140:82:1;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;18140:82:1;;;;;;;;-1:-1:-1;;18161:19:1;-1:-1:-1;;;;;18140:48:1;;-1:-1:-1;;18140:82:1;;;;;;;;;;;;;;;:48;:82;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;18140:82:1;18233:11;;;;;;;;;;18140:82;;-1:-1:-1;18233:11:1;;;;;;18140:82;18233:11;;;17991:258;;;;;;:::o;3747:1692:32:-;3878:14;3894:16;3926:17;401:2:22;3926:42:32;;4138:11;4132:4;4127:23;4120:31;4116:36;4112:1;4106:4;4102:12;4098:55;4084:11;4078:4;4073:23;4069:85;4061:93;;4051:111;4178:3;4174:1261;;;4397:1;4393:12;;;4435:15;;;;;;4278;4435;;;;;;;;;;;4366:4;4356:15;;;;4455;;;4435:36;;;;4542:9;;4538:238;;4583:27;4605:4;4583:21;:27::i;:::-;4577:3;:33;4571:40;;4563:48;;;;4629:28;4652:4;4629:22;:28::i;:::-;4659:4;4621:43;;;;;;;;;4538:238;4703:9;4697:16;;4689:24;;;;4731:28;4754:4;4731:22;:28::i;:::-;4761:5;4723:44;;;;;;;;;4174:1261;4895:1;4887:9;;;5023:1;5019:12;;;5117:15;;;;;;4904;5117;;;;;;;;;;;4887:9;;5019:12;4992:4;4982:15;;;5117:30;;5160:9;;5156:273;;5195:37;5227:4;5220;5219:5;;:12;5195:23;:37::i;:::-;5189:44;;5181:52;;;;5276:28;5299:4;5276:22;:28::i;5156:273::-;5356:9;5350:3;:15;5344:22;;5336:30;;;;5384:28;5407:4;5384:22;:28::i;4174:1261::-;3747:1692;;;;;;:::o;1453:2484:31:-;1516:13;1571:16;;;;1580:6;1571:16;;1620:11;;;1619:20;;;1611:29;;762:9;1654:28;;;1646:42;;;;;-1:-1:-1;;;1646:42:31;;;;;;;;;;;;-1:-1:-1;;;1646:42:31;;;;;;;;;;;;;;;1695:13;1721:3;1711:13;;:93;;-1:-1:-1;;;1711:93:31;;;1732:34;1711:93;-1:-1:-1;;;;;1695:109:31;;-1:-1:-1;1824:3:31;1814:13;;:18;1810:83;;1851:34;1843:42;1890:3;1842:51;1810:83;1913:3;1903:13;;:18;1899:83;;1940:34;1932:42;1979:3;1931:51;1899:83;2002:3;1992:13;;:18;1988:83;;2029:34;2021:42;2068:3;2020:51;1988:83;2091:4;2081:14;;:19;2077:84;;2119:34;2111:42;2158:3;2110:51;2077:84;2181:4;2171:14;;:19;2167:84;;2209:34;2201:42;2248:3;2200:51;2167:84;2271:4;2261:14;;:19;2257:84;;2299:34;2291:42;2338:3;2290:51;2257:84;2361:4;2351:14;;:19;2347:84;;2389:34;2381:42;2428:3;2380:51;2347:84;2451:5;2441:15;;:20;2437:85;;2480:34;2472:42;2519:3;2471:51;2437:85;2542:5;2532:15;;:20;2528:85;;2571:34;2563:42;2610:3;2562:51;2528:85;2633:5;2623:15;;:20;2619:85;;2662:34;2654:42;2701:3;2653:51;2619:85;2724:5;2714:15;;:20;2710:85;;2753:34;2745:42;2792:3;2744:51;2710:85;2815:6;2805:16;;:21;2801:86;;2845:34;2837:42;2884:3;2836:51;2801:86;2907:6;2897:16;;:21;2893:86;;2937:34;2929:42;2976:3;2928:51;2893:86;2999:6;2989:16;;:21;2985:86;;3029:34;3021:42;3068:3;3020:51;2985:86;3091:6;3081:16;;:21;3077:86;;3121:34;3113:42;3160:3;3112:51;3077:86;3183:7;3173:17;;:22;3169:86;;3214:33;3206:41;3252:3;3205:50;3169:86;3275:7;3265:17;;:22;3261:85;;3306:32;3298:40;3343:3;3297:49;3261:85;3366:7;3356:17;;:22;3352:83;;3397:30;3389:38;3432:3;3388:47;3352:83;3455:7;3445:17;;:22;3441:78;;3486:25;3478:33;3516:3;3477:42;3441:78;3537:1;3530:4;:8;;;3526:47;;;3568:5;-1:-1:-1;;3568:5:31;3548:25;;;;;3540:33;;3526:47;3900:17;;;:22;:30;;3929:1;3900:30;;;3925:1;3900:30;3883:48;;3893:2;3884:5;:11;;3883:48;3867:65;;1453:2484;;;;;;:::o;5768:2295:27:-;5984:19;6011:13;6032:14;6054:17;6086:69;6158:9;:47;;6189:16;6158:47;;;6170:16;6158:47;6086:119;;6235:1;6216:15;:20;6212:1847;;6270:31;6304:57;6328:15;6352:3;6346:9;;:3;:9;6304:57;;6357:3;6304:15;:57::i;:::-;6270:91;;6377:48;6388:11;6401:12;6415:9;6377:10;:48;;:::i;:::-;6369:56;;6464:5;6437:23;:32;6433:630;;6495:11;;-1:-1:-1;6528:48:27;6554:5;6528:48;;;;6566:3;:9;;;6528:48;:25;:48::i;:::-;6516:60;;6433:630;;;6615:82;6637:12;6651:9;6662:23;6687:9;6615:21;:82::i;:::-;6601:96;-1:-1:-1;;;;;;6711:26:27;;;;;;;6707:348;;6759:48;6770:11;6783:12;6797:9;6759:10;:48;;:::i;:::-;6751:56;;6950:5;6931:15;6923:32;6911:44;;6707:348;;;6996:48;7022:5;6996:48;;;;7034:3;:9;;;6996:48;:25;:48::i;:::-;6984:60;;6707:348;7080:87;7130:11;7143:12;7157:9;7081;:47;;7112:16;7081:47;;;7093:16;7081:47;7080:87;;:::i;:::-;7071:96;;6212:1847;;;;7188:69;7260:9;:47;;7291:16;7260:47;;;7272:16;7260:47;7188:119;;7325:48;7336:11;7349:12;7363:9;7325:10;:48;;:::i;:::-;7316:57;;7400:15;7399:16;;7381:34;;7455:6;7435:15;7427:34;7423:497;;7477:11;7463:25;;7423:497;;;7525:84;7548:12;7562:9;7581:15;7599:9;7525:22;:84::i;:::-;7511:98;-1:-1:-1;;;;;;7624:26:27;;;;;;;7620:112;;7673:48;7684:11;7697:12;7711:9;7673:10;:48;;:::i;:::-;7664:57;;7620:112;7838:15;7821:6;:33;7817:95;;;7885:15;7868:33;;7817:95;7936:48;7947:11;7960:12;7974:9;7936:10;:48;;:::i;:::-;7928:56;-1:-1:-1;8004:48:27;7928:56;8004:48;;;;8042:3;:9;;;8004:48;:25;:48::i;:::-;7992:60;;6212:1847;;5768:2295;;;;;;;;;;;;:::o;1021:111:28:-;1073:8;-1:-1:-1;;;1097:10:28;;1089:19;;;;;;-1:-1:-1;1125:1:28;1021:111::o;1351:114:26:-;1436:5;;;1431:16;;;;1407:8;1452:6;;;1431:28;1423:37;;;;;1077:119;1135:9;1160:6;;;:30;;-1:-1:-1;;1175:5:26;;;1189:1;1184;1175:5;1184:1;1170:15;;;;;:20;1160:30;1152:39;;;;;6525:744:30;6822:10;;;;;;6773:21;6822:10;;;;;;;;;;;;6871:22;;;;;-1:-1:-1;;;;;;;;6871:22:30;-1:-1:-1;;;6871:22:30;;;;;6864:29;;;6839:54;;;;-1:-1:-1;;;;6839:54:30;;;;;;;6963:29;;;;;6931:61;;;6899:93;;;;-1:-1:-1;;;;;;;;6899:93:30;;;;;;;;7042:24;;;;7025:41;;;6998:68;;;;;-1:-1:-1;;6998:68:30;;;;;;;;7124:25;;;;;7101:48;;;7073:76;;;-1:-1:-1;7206:25:30;;;;7183:48;;;7155:76;;7245:19;-1:-1:-1;;;7245:19:30;;;;;6525:744::o;506:210:25:-;568:9;593:1;589;:5;;;585:127;;;-1:-1:-1;629:2:25;;;;617:15;;-1:-1:-1;;;;;612:25:25;;;;;;;604:40;;;;;-1:-1:-1;;;604:40:25;;;;;;;;;;;;-1:-1:-1;;;604:40:25;;;;;;;;;;;;;;;585:127;;;-1:-1:-1;678:14:25;;;-1:-1:-1;;;;;673:25:25;;;;;;;;665:40;;;;;-1:-1:-1;;;665:40:25;;;;;;;;;;;;-1:-1:-1;;;665:40:25;;;;;;;;;;;;;;4246:1554:30;4601:10;;;;;;4561:12;4601:10;;;;;;;;;;4648:19;;-1:-1:-1;;;4648:19:30;;;;;-1:-1:-1;;;;;4704:19:30;4561:12;4760:60;4704:19;4805:14;4760:22;:60::i;:::-;4730:90;-1:-1:-1;4856:36:30;-1:-1:-1;;;;;4834:58:30;;;4826:73;;;;;-1:-1:-1;;;4826:73:30;;;;;;;;;;;;-1:-1:-1;;;4826:73:30;;;;;;;;;;;;;;;5039:5;:141;;5121:59;:48;;:28;;;;:48;;;:32;:48::i;:::-;:57;:59::i;:::-;5039:141;;;5053:59;:48;;:28;;;;:48;;;:32;:48::i;:59::-;5017:163;;-1:-1:-1;;;;;;;;;;;5017:163:30;;;;;;;;;;-1:-1:-1;;;5017:163:30;;;;;5187:41;;;;;;;;;;;;5246:24;;-1:-1:-1;5281:25:30;;5277:519;;5327:7;5326:8;5316:18;;5460:11;5452:19;;:4;:19;;;5448:311;;5483:25;;;:48;;;5541:25;;;:48;;;5599:29;;;:61;;-1:-1:-1;;;;;;;;5599:61:30;-1:-1:-1;;;;;;;;5599:61:30;;;;-1:-1:-1;;5670:41:30;-1:-1:-1;5670:41:30;;;;;;-1:-1:-1;;;;5721:29:30;-1:-1:-1;;;;5721:29:30;;;;;;5448:311;5766:16;;;:23;;-1:-1:-1;;;;;5766:23:30;-1:-1:-1;;;5766:23:30;;;5277:519;4246:1554;;;;;;;;;;;;;;;;:::o;569:414:32:-;401:2:22;664:29:32;;;;;:34;;;656:65;;;;;-1:-1:-1;;;656:65:32;;;;;;;;;;;;-1:-1:-1;;;656:65:32;;;;;;;;;;;;;;;401:2:22;761:30:32;;;;;;926:1;922:12;;;964:1;945:15;;;;;814;945;;;;;;;;;;;;:33;;897:4;887:15;;;964:14;;;;945:33;;;569:414::o;2123:1060:30:-;2437:16;;;;;;2350:28;2437:16;;;;;;;;;;;2480:13;;;;;;;;;;;2350:28;;;;2437:16;;2504:21;;;;;2500:679;;;2554:10;2539:25;;:11;:25;;;2535:337;;2622:5;:26;;;2599:20;:49;2576:72;;2704:5;:26;;;2681:20;:49;2658:72;;2535:337;;;2778:5;:26;;;2755:49;;2837:5;:26;;;2814:49;;2535:337;2903:26;;;;2961;;;;2879:50;;;2937;;;;2500:679;;;3060:5;:26;;;3031:5;:26;;;:55;3008:78;;3146:5;:26;;;3117:5;:26;;;:55;3094:78;;2500:679;2123:1060;;;;;;;;;;;:::o;6840:1845:1:-;7080:19;;-1:-1:-1;;;;;7080:19:1;;;-1:-1:-1;;;7101:35:1;;;;7148:19;;;;7144:686;;-1:-1:-1;;;;;7185:20:1;;7177:35;;;;;-1:-1:-1;;;7177:35:1;;;;;;;;;;;;-1:-1:-1;;;7177:35:1;;;;;;;;;;;;;;;7144:686;;;7293:1;7276:14;:18;;;7272:226;;;7334:17;;;;7365:22;;7361:129;;7460:18;7409:69;;7430:25;7410:17;:15;:17::i;:::-;:45;7409:69;;;;7401:78;;;;;;7272:226;;7541:21;7565:56;7588:16;7606:14;7565:22;:56::i;:::-;7541:80;-1:-1:-1;7541:80:1;-1:-1:-1;;;;;7723:17:1;;:92;;7814:1;7723:92;;;7761:1;7744:14;:18;;;:66;;7785:25;7744:66;;;7765:17;:15;:17::i;:::-;7629:194;;-1:-1:-1;;;;7629:194:1;;;;;;-1:-1:-1;;;7629:194:1;;-1:-1:-1;;;;;;7629:194:1;-1:-1:-1;;;;;7629:194:1;;;;;;;-1:-1:-1;7144:686:1;7895:30;;;;7963;;;;7863:29;8022:45;;;8018:239;;8077:30;;;:53;;;8154:95;8170:44;;;-1:-1:-1;;;;;8154:95:1;;-1:-1:-1;;;8154:15:1;:95::i;:::-;8138:112;;8018:239;8262:13;8309:21;8285:20;:45;8281:239;;8340:30;;;:53;;;8417:95;8433:44;;;-1:-1:-1;;;;;8417:95:1;;-1:-1:-1;;;8417:15:1;:95::i;:::-;8401:112;;8281:239;8590:13;;;-1:-1:-1;;;;;8590:18:1;;8586:95;;8618:15;;;:24;;-1:-1:-1;;;;;;8618:24:1;;-1:-1:-1;;;;;8618:24:1;;;;;;;;8650;;;-1:-1:-1;;;8650:24:1;;;;;;;;;;;;;;;8586:95;6840:1845;;;;;;;;;;:::o;2670:351:33:-;2791:18;2844:1;2831:9;:14;;;;:185;;2939:77;:66;2954:10;2966;2987:9;2986:10;;2999:5;2939:14;:66::i;:::-;:75;:77::i;:::-;2938:78;;2831:185;;;2854:75;:64;2869:10;2881;2901:9;2913:4;2854:14;:64::i;:75::-;2817:199;2670:351;-1:-1:-1;;;;2670:351:33:o;3364:::-;3485:18;3538:1;3525:9;:14;;;;:185;;3633:77;:66;3648:10;3660;3681:9;3680:10;;3693:5;3633:14;:66::i;3525:185::-;3548:75;:64;3563:10;3575;3595:9;3607:4;3548:14;:64::i;2586:495:32:-;2716:1;2712:12;;;2703:22;;;2753:1;2749:12;;;2740:22;2790:1;2786:12;;;2777:22;2827:1;2823:12;;;2814:22;2864:2;2860:13;;;2851:23;2902:2;2898:13;;;2889:23;2940:2;2936:13;;;2927:23;2978:3;2974:14;;;2965:24;3014:12;;;;3004:23;;2654:16;3046:29;3004:23;3046;:29::i;5443:316::-;401:2:22;5548:29:32;;-1:-1:-1;;5587:31:32;;;;;5583:172;;;-1:-1:-1;;;5583:172:32;;;762:9:31;5676:31:32;;;;;5672:83;;;-1:-1:-1;762:9:31;5443:316:32;;;:::o;1185:1162::-;1331:66;1321:77;;1314:85;-1:-1:-1;;;;;1453:77:32;;1446:85;1443:1;1439:93;1422:111;-1:-1:-1;;;;;;;;;;;;;1587:77:32;;1580:85;1577:1;1573:93;1556:111;1731:66;1721:77;;1714:85;1711:1;1707:93;1690:111;1865:66;1855:77;;1848:85;1845:1;1841:93;1824:111;1999:66;1989:77;;1982:85;1979:1;1975:93;1958:111;2133:66;2123:77;;2116:85;2113:1;2109:93;2092:111;2267:66;2257:77;;;;2250:85;2247:1;2243:93;2226:111;;1290:1053::o;4119:193:27:-;4229:7;4251:56;4281:4;4287:2;4291:9;4302:4;4251:29;:56::i;3922:193::-;4032:7;4054:56;4084:2;4088:4;4094:9;4105:4;4054:29;:56::i;920:231::-;1059:19;1093:53;1105:5;1112:9;1123:5;1130:9;1141:4;1093:11;:53::i;4514:194::-;4624:7;4646:57;4676:4;4682:2;4686:9;4697:5;4646:29;:57::i;4316:194::-;4426:7;4448:57;4478:2;4482:4;4488:9;4499:5;4448:29;:57::i;1701:235::-;1842:19;1876:55;1888:5;1895:9;1906:6;1914:9;1925:5;1876:11;:55::i;887:586:33:-;1027:19;-1:-1:-1;;;;;1075:23:33;;;1054:44;;;1112:23;;;;1104:32;;;;;;-1:-1:-1;1207:42:33;;;-1:-1:-1;;;;;;;1207:42:33;1270:7;:198;;-1:-1:-1;;;;;1398:70:33;;;;:57;;1414:10;;1426:16;;1398:57;;:15;:57::i;:::-;:70;;;;;;1270:198;;;1286:103;1309:67;1335:10;1347:16;-1:-1:-1;;;;;1309:67:33;;:25;:67::i;:::-;-1:-1:-1;;;;;1286:103:33;;:22;:103::i;:::-;1256:212;887:586;-1:-1:-1;;;;;;;887:586:33:o;1923:404::-;2063:19;-1:-1:-1;;;;;2098:24:33;;;;;;;;2090:33;;;;;;2150:23;;;-1:-1:-1;;;;;2129:44:33;2193:7;:129;;2269:53;2285:10;-1:-1:-1;;;;;2269:53:33;;-1:-1:-1;;;2269:15:33;:53::i;:::-;2193:129;;;2203:63;2229:10;-1:-1:-1;;;;;2203:63:33;;-1:-1:-1;;;2203:25:33;:63::i;:::-;2179:143;1923:404;-1:-1:-1;;;;;;1923:404:33:o;1940:1978:27:-;2090:19;-1:-1:-1;;;;;2125:9:27;;2117:18;;;;;;-1:-1:-1;;;;;2149:13:27;;2141:22;;;;;;2187:9;2174:22;;:9;:22;;;2170:1744;;;2239:11;2235:29;;-1:-1:-1;2259:5:27;2252:12;;2235:29;-1:-1:-1;2299:42:27;;;-1:-1:-1;;;;;;;2299:42:27;2350:830;;;;-1:-1:-1;;;;;2404:44:27;;2415:14;;;;;;;2404:35;;;;;:44;2400:276;;;2484:26;;;2526:31;;;2522:116;;2574:63;2600:16;-1:-1:-1;;;;;2574:63:27;;2625:11;2574:25;:63::i;:::-;2559:79;;;;;;;2522:116;2400:276;;2701:80;2724:16;2742:38;2773:6;-1:-1:-1;;;;;2743:24:27;;2724:16;2743:24;;;;;;;2742:30;:38::i;:::-;2701:22;:80::i;:::-;2686:96;;;;;;2350:830;-1:-1:-1;;;;;2840:44:27;;2851:14;;;;;;;2840:35;;;;;:44;2832:53;;;;;;2986:7;2967:16;:26;2959:35;;;;;;3081:90;:78;3107:16;-1:-1:-1;;;;;3081:78:27;;3132:26;;;3081:25;:78::i;:::-;:88;:90::i;2170:1744::-;3354:9;3350:558;;;3392:184;:159;-1:-1:-1;;;;;3424:27:27;;;:126;;3501:49;3517:6;-1:-1:-1;;;;;;;;3501:49:27;;:15;:49::i;:::-;3424:126;;;-1:-1:-1;;;;;3454:44:27;;129:2:22;3455:30:27;;;3454:44;;;;;;3424:126;-1:-1:-1;;;;;3392:14:27;;;:31;:159::i;:184::-;3375:201;;;;3350:558;3601:16;-1:-1:-1;;;;;3620:27:27;;;:177;;3738:59;3764:6;-1:-1:-1;;;;;;;;3738:59:27;;:25;:59::i;:::-;3620:177;;;3660:65;129:2:22;3683:30:27;;;-1:-1:-1;;;;;3660:65:27;;:22;:65::i;:::-;3601:196;-1:-1:-1;;;;;;3816:16:27;;;-1:-1:-1;3808:25:27;;;;;;-1:-1:-1;;;;;3858:16:27;;;;-1:-1:-1;3843:32:27;;5064:149:24;5175:9;;;5189;;5186:16;;5171:32;;5158:51::o;482:105:28:-;580:1;-1:-1:-1;;;;;560:21:28;;;;552:30;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;
Swarm Source
none://164736f6c6343000706000a
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.