// SPDX-License-Identifier: BUSL-1.1pragmasolidity =0.7.6;import'./Constants.sol';
/// @title AdaptiveFee/// @notice Calculates fee based on combination of sigmoidslibraryAdaptiveFee{
// alpha1 + alpha2 + baseFee must be <= type(uint16).maxstructConfiguration {
uint16 alpha1; // max value of the first sigmoiduint16 alpha2; // max value of the second sigmoiduint32 beta1; // shift along the x-axis for the first sigmoiduint32 beta2; // shift along the x-axis for the second sigmoiduint16 gamma1; // horizontal stretch factor for the first sigmoiduint16 gamma2; // horizontal stretch factor for the second sigmoiduint32 volumeBeta; // shift along the x-axis for the outer volume-sigmoiduint16 volumeGamma; // horizontal stretch factor the outer volume-sigmoiduint16 baseFee; // minimum possible fee
}
/// @notice Calculates fee based on formula:/// baseFee + sigmoidVolume(sigmoid1(volatility, volumePerLiquidity) + sigmoid2(volatility, volumePerLiquidity))/// maximum value capped by baseFee + alpha1 + alpha2functiongetFee(uint88 volatility,
uint256 volumePerLiquidity,
Configuration memory config
) internalpurereturns (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;
}
returnuint16(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 alphafunctionsigmoid(uint256 x,
uint16 g,
uint16 alpha,
uint256 beta
) internalpurereturns (uint256 res) {
if (x > beta) {
x = x - beta;
if (x >=6*uint256(g)) return alpha; // so x < 19 bitsuint256 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)) return0; // so x < 19 bitsuint256 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!) + ...functionexp(uint256 x,
uint16 g,
uint256 gHighestDegree
) internalpurereturns (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 bitsuint256 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);
}
}
Contract Source Code
File 2 of 30: AlgebraPool.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity =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';
contractAlgebraPoolisPoolState, PoolImmutables, IAlgebraPool{
usingLowGasSafeMathforuint256;
usingLowGasSafeMathforint256;
usingLowGasSafeMathforuint128;
usingSafeCastforuint256;
usingSafeCastforint256;
usingTickTableformapping(int16=>uint256);
usingTickManagerformapping(int24=>TickManager.Tick);
structPosition {
uint128 liquidity; // The amount of liquidity concentrated in the rangeuint32 lastLiquidityAddTimestamp; // Timestamp of last adding of liquidityuint256 innerFeeGrowth0Token; // The last updated fee growth per unit of liquidityuint256 innerFeeGrowth1Token;
uint128 fees0; // The amount of token0 owed to a LPuint128 fees1; // The amount of token1 owed to a LP
}
/// @inheritdoc IAlgebraPoolStatemapping(bytes32=> Position) publicoverride positions;
/// @dev Restricts everyone calling a function except factory ownermodifieronlyFactoryOwner() {
require(msg.sender== IAlgebraFactory(factory).owner());
_;
}
modifieronlyValidTicks(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;
}
functionbalanceToken0() privateviewreturns (uint256) {
return IERC20Minimal(token0).balanceOf(address(this));
}
functionbalanceToken1() privateviewreturns (uint256) {
return IERC20Minimal(token1).balanceOf(address(this));
}
/// @inheritdoc IAlgebraPoolStatefunctiontimepoints(uint256 index)
externalviewoverridereturns (bool initialized,
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint88 volatilityCumulative,
int24 averageTick,
uint144 volumePerLiquidityCumulative
)
{
return IDataStorageOperator(dataStorageOperator).timepoints(index);
}
structCumulatives {
int56 tickCumulative;
uint160 outerSecondPerLiquidity;
uint32 outerSecondsSpent;
}
/// @inheritdoc IAlgebraPoolDerivedStatefunctiongetInnerCumulatives(int24 bottomTick, int24 topTick)
externalviewoverrideonlyValidTicks(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 IAlgebraPoolDerivedStatefunctiongetTimepoints(uint32[] calldata secondsAgos)
externalviewoverridereturns (int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
)
{
return
IDataStorageOperator(dataStorageOperator).getTimepoints(
_blockTimestamp(),
secondsAgos,
globalState.tick,
globalState.timepointIndex,
liquidity
);
}
/// @inheritdoc IAlgebraPoolActionsfunctioninitialize(uint160 initialPrice) externaloverride{
require(globalState.price ==0, 'AI');
// getTickAtSqrtRatio checks validity of initialPrice insideint24 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 liquidityuint128 liquidityNext = LiquidityMath.addDelta(currentLiquidity, liquidityDelta);
(_position.liquidity, _position.lastLiquidityAddTimestamp) = (
liquidityNext,
liquidityNext >0 ? (liquidityDelta >0 ? _blockTimestamp() : lastLiquidityAddTimestamp) : 0
);
}
// update the positionuint256 _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 itif (fees0 | fees1 !=0) {
_position.fees0 += fees0;
_position.fees1 += fees1;
}
}
structUpdatePositionCache {
uint160 price; // The square root of the current price in Q64.96 formatint24 tick; // The current tickuint16 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
)
privatereturns (
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 itif (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
)
privatepurereturns (int256 amount0,
int256 amount1,
int128 globalLiquidityDelta
)
{
// If current tick is less than the provided bottom one then only the token0 has to be providedif (currentTick < bottomTick) {
amount0 = TokenDeltaMath.getToken0Delta(TickMath.getSqrtRatioAtTick(bottomTick), TickMath.getSqrtRatioAtTick(topTick), liquidityDelta);
} elseif (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 providedelse {
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
*/functiongetOrCreatePosition(address owner,
int24 bottomTick,
int24 topTick
) privateviewreturns (Position storage) {
bytes32 key;
assembly {
key :=or(shl(24, or(shl(24, owner), and(bottomTick, 0xFFFFFF))), and(topTick, 0xFFFFFF))
}
return positions[key];
}
/// @inheritdoc IAlgebraPoolActionsfunctionmint(address sender,
address recipient,
int24 bottomTick,
int24 topTick,
uint128 liquidityDesired,
bytescalldata data
)
externaloverridelockonlyValidTicks(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 IAlgebraPoolActionsfunctioncollect(address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount0Requested,
uint128 amount1Requested
) externaloverridelockreturns (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 IAlgebraPoolActionsfunctionburn(int24 bottomTick,
int24 topTick,
uint128 amount
) externaloverridelockonlyValidTicks(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 sigmoidsfunction_getNewFee(uint32 _time,
int24 _tick,
uint16 _index,
uint128 _liquidity
) privatereturns (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
) privatereturns (uint16 newTimepointIndex) {
return IDataStorageOperator(dataStorageOperator).write(timepointIndex, blockTimestamp, tick, liquidity, volumePerLiquidityInBlock);
}
function_getSingleTimepoint(uint32 blockTimestamp,
uint32 secondsAgo,
int24 startTick,
uint16 timepointIndex,
uint128 liquidityStart
)
privateviewreturns (int56 tickCumulative,
uint160 secondsPerLiquidityCumulative,
uint112 volatilityCumulative,
uint256 volumePerAvgLiquidity
)
{
return IDataStorageOperator(dataStorageOperator).getSingleTimepoint(blockTimestamp, secondsAgo, startTick, timepointIndex, liquidityStart);
}
function_swapCallback(int256 amount0,
int256 amount1,
bytescalldata data
) private{
IAlgebraSwapCallback(msg.sender).algebraSwapCallback(amount0, amount1, data);
}
/// @inheritdoc IAlgebraPoolActionsfunctionswap(address recipient,
bool zeroToOne,
int256 amountRequired,
uint160 limitSqrtPrice,
bytescalldata data
) externaloverridereturns (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 recipientuint256 balance0Before = balanceToken0();
_swapCallback(amount0, amount1, data); // callback to get tokens from the callerrequire(balance0Before.add(uint256(amount0)) <= balanceToken0(), 'IIA');
} else {
if (amount0 <0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); // transfer to recipientuint256 balance1Before = balanceToken1();
_swapCallback(amount0, amount1, data); // callback to get tokens from the callerrequire(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 IAlgebraPoolActionsfunctionswapSupportingFeeOnInputTokens(address sender,
address recipient,
bool zeroToOne,
int256 amountRequired,
uint160 limitSqrtPrice,
bytescalldata data
) externaloverridereturns (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 recipientif (zeroToOne) {
if (amount1 <0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1));
// return the leftoversif (amount0 < amountRequired) TransferHelper.safeTransfer(token0, sender, uint256(amountRequired.sub(amount0)));
} else {
if (amount0 <0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0));
// return the leftoversif (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
}
structSwapCalculationCache {
uint256 communityFee; // The community fee of the selling token, uint256 to minimize castsuint128 volumePerLiquidityInBlock;
int56 tickCumulative; // The global tickCumulative at the momentuint160 secondsPerLiquidityCumulative; // The global secondPerLiquidity at the momentbool computedLatestTimepoint; // if we have already fetched _tickCumulative_ and _secondPerLiquidity_ from the DataOperatorint256 amountRequiredInitial; // The initial value of the exact input\output amountint256 amountCalculated; // The additive amount of total output\input calculated trough the swapuint256 totalFeeGrowth; // The initial totalFeeGrowth + the fee growth during a swapuint256 totalFeeGrowthB;
IAlgebraVirtualPool.Status incentiveStatus; // If there is an active incentive at the momentbool exactInput; // Whether the exact input or output is specifieduint16 fee; // The current dynamic feeint24 startTick; // The tick at the start of a swapuint16 timepointIndex; // The index of last written timepoint
}
structPriceMovementCache {
uint160 stepSqrtPrice; // The Q64.96 sqrt of the price at the start of the stepint24 nextTick; // The tick till the current step goesbool initialized; // True if the _nextTick is initializeduint160 nextTickPrice; // The Q64.96 sqrt of the price calculated from the _nextTickuint256 input; // The additive amount of tokens that have been provideduint256 output; // The additive amount of token that have been withdrawnuint256 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
)
privatereturns (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 functionrequire(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);
} elseif (_status == IAlgebraVirtualPool.Status.ACTIVE) {
cache.incentiveStatus = IAlgebraVirtualPool.Status.ACTIVE;
} elseif (_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 blockif (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 limitwhile (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 itif (step.initialized) {
// once at a swap we have to get the last timepoint of the observationif (!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 poolif (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;
} elseif (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 conditionif (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 IAlgebraPoolActionsfunctionflash(address recipient,
uint256 amount0,
uint256 amount1,
bytescalldata data
) externaloverridelock{
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 IAlgebraPoolPermissionedActionsfunctionsetCommunityFee(uint8 communityFee0, uint8 communityFee1) externaloverridelockonlyFactoryOwner{
require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE));
(globalState.communityFeeToken0, globalState.communityFeeToken1) = (communityFee0, communityFee1);
emit CommunityFee(communityFee0, communityFee1);
}
/// @inheritdoc IAlgebraPoolPermissionedActionsfunctionsetIncentive(address virtualPoolAddress) externaloverride{
require(msg.sender== IAlgebraFactory(factory).farmingAddress());
activeIncentive = virtualPoolAddress;
emit Incentive(virtualPoolAddress);
}
/// @inheritdoc IAlgebraPoolPermissionedActionsfunctionsetLiquidityCooldown(uint32 newLiquidityCooldown) externaloverrideonlyFactoryOwner{
require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown);
liquidityCooldown = newLiquidityCooldown;
emit LiquidityCooldown(newLiquidityCooldown);
}
}
Contract Source Code
File 3 of 30: Constants.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity =0.7.6;libraryConstants{
uint8internalconstant RESOLUTION =96;
uint256internalconstant Q96 =0x1000000000000000000000000;
uint256internalconstant Q128 =0x100000000000000000000000000000000;
// fee value in hundredths of a bip, i.e. 1e-6uint16internalconstant BASE_FEE =100;
int24internalconstant TICK_SPACING =60;
// max(uint128) / ( (MAX_TICK - MIN_TICK) / TICK_SPACING )uint128internalconstant MAX_LIQUIDITY_PER_TICK =11505743598341114571880798222544994;
uint32internalconstant MAX_LIQUIDITY_COOLDOWN =1days;
uint8internalconstant MAX_COMMUNITY_FEE =250;
uint256internalconstant COMMUNITY_FEE_DENOMINATOR =1000;
}
Contract Source Code
File 4 of 30: FullMath.sol
// SPDX-License-Identifier: MITpragmasolidity ^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 bitslibraryFullMath{
/// @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/muldivfunctionmulDiv(uint256 a,
uint256 b,
uint256 denominator
) internalpurereturns (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 + prod0uint256 prod0 = a * b; // Least significant 256 bits of the productuint256 prod1; // Most significant 256 bits of the productassembly {
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 == 0require(denominator > prod1);
// Handle non-overflow cases, 256 by 256 divisionif (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 numberassembly {
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 twoassembly {
denominator :=div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of twoassembly {
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 oneassembly {
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**4uint256 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 resultfunctionmulDivRoundingUp(uint256 a,
uint256 b,
uint256 denominator
) internalpurereturns (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)functiondivRoundingUp(uint256 x, uint256 y) internalpurereturns (uint256 z) {
assembly {
z :=add(div(x, y), gt(mod(x, y), 0))
}
}
}
Contract Source Code
File 5 of 30: IAlgebraFactory.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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
*/interfaceIAlgebraFactory{
/**
* @notice Emitted when the owner of the factory is changed
* @param newOwner The owner after the owner was changed
*/eventOwner(addressindexed newOwner);
/**
* @notice Emitted when the vault address is changed
* @param newVaultAddress The vault address after the address was changed
*/eventVaultAddress(addressindexed 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
*/eventPool(addressindexed token0, addressindexed token1, address pool);
/**
* @notice Emitted when the farming address is changed
* @param newFarmingAddress The farming address after the address was changed
*/eventFarmingAddress(addressindexed newFarmingAddress);
eventFeeConfiguration(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
*/functionowner() externalviewreturns (address);
/**
* @notice Returns the current poolDeployerAddress
* @return The address of the poolDeployer
*/functionpoolDeployer() externalviewreturns (address);
/**
* @dev Is retrieved from the pools to restrict calling
* certain functions not by a tokenomics contract
* @return The tokenomics contract address
*/functionfarmingAddress() externalviewreturns (address);
functionvaultAddress() externalviewreturns (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
*/functionpoolByPair(address tokenA, address tokenB) externalviewreturns (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
*/functioncreatePool(address tokenA, address tokenB) externalreturns (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
*/functionsetOwner(address _owner) external;
/**
* @dev updates tokenomics address on the factory
* @param _farmingAddress The new tokenomics contract address
*/functionsetFarmingAddress(address _farmingAddress) external;
/**
* @dev updates vault address on the factory
* @param _vaultAddress The new vault contract address
*/functionsetVaultAddress(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
*/functionsetBaseFeeConfiguration(uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
) external;
}
Contract Source Code
File 6 of 30: IAlgebraFlashCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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
*/interfaceIAlgebraFlashCallback{
/**
* @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
*/functionalgebraFlashCallback(uint256 fee0,
uint256 fee1,
bytescalldata data
) external;
}
Contract Source Code
File 7 of 30: IAlgebraMintCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/interfacesinterfaceIAlgebraMintCallback{
/// @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 callfunctionalgebraMintCallback(uint256 amount0Owed,
uint256 amount1Owed,
bytescalldata data
) external;
}
Contract Source Code
File 8 of 30: IAlgebraPool.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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
*/interfaceIAlgebraPoolisIAlgebraPoolImmutables,
IAlgebraPoolState,
IAlgebraPoolDerivedState,
IAlgebraPoolActions,
IAlgebraPoolPermissionedActions,
IAlgebraPoolEvents{
// used only for combining interfaces
}
Contract Source Code
File 9 of 30: IAlgebraPoolActions.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/interfacesinterfaceIAlgebraPoolActions{
/**
* @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
*/functioninitialize(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
*/functionmint(address sender,
address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount,
bytescalldata data
)
externalreturns (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
*/functioncollect(address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount0Requested,
uint128 amount1Requested
) externalreturns (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
*/functionburn(int24 bottomTick,
int24 topTick,
uint128 amount
) externalreturns (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
*/functionswap(address recipient,
bool zeroToOne,
int256 amountSpecified,
uint160 limitSqrtPrice,
bytescalldata data
) externalreturns (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
*/functionswapSupportingFeeOnInputTokens(address sender,
address recipient,
bool zeroToOne,
int256 amountSpecified,
uint160 limitSqrtPrice,
bytescalldata data
) externalreturns (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
*/functionflash(address recipient,
uint256 amount0,
uint256 amount1,
bytescalldata data
) external;
}
Contract Source Code
File 10 of 30: IAlgebraPoolDeployer.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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
*/interfaceIAlgebraPoolDeployer{
/**
* @notice Emitted when the factory address is changed
* @param factory The factory address after the address was changed
*/eventFactory(addressindexed 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
*/functionparameters()
externalviewreturns (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
*/functiondeploy(address dataStorage,
address factory,
address token0,
address token1
) externalreturns (address pool);
/**
* @dev Sets the factory address to the poolDeployer for permissioned actions
* @param factory The address of the Algebra factory
*/functionsetFactory(address factory) external;
}
Contract Source Code
File 11 of 30: IAlgebraPoolDerivedState.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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
*/interfaceIAlgebraPoolDerivedState{
/**
* @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`
*/functiongetTimepoints(uint32[] calldata secondsAgos)
externalviewreturns (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
*/functiongetInnerCumulatives(int24 bottomTick, int24 topTick)
externalviewreturns (int56 innerTickCumulative,
uint160 innerSecondsSpentPerLiquidity,
uint32 innerSecondsSpent
);
}
Contract Source Code
File 12 of 30: IAlgebraPoolEvents.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/interfacesinterfaceIAlgebraPoolEvents{
/**
* @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
*/eventInitialize(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
*/eventMint(address sender,
addressindexed owner,
int24indexed bottomTick,
int24indexed 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
*/eventCollect(addressindexed owner, address recipient, int24indexed bottomTick, int24indexed 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
*/eventBurn(addressindexed owner, int24indexed bottomTick, int24indexed 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
*/eventSwap(addressindexed sender, addressindexed 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
*/eventFlash(addressindexed sender, addressindexed 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
*/eventCommunityFee(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
*/eventIncentive(addressindexed virtualPoolAddress);
/**
* @notice Emitted when the fee changes
* @param fee The value of the token fee
*/eventFee(uint16 fee);
/**
* @notice Emitted when the LiquidityCooldown changes
* @param liquidityCooldown The value of locktime for added liquidity
*/eventLiquidityCooldown(uint32 liquidityCooldown);
}
Contract Source Code
File 13 of 30: IAlgebraPoolImmutables.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/interfacesinterfaceIAlgebraPoolImmutables{
/**
* @notice The contract that stores all the timepoints and can perform actions with them
* @return The operator address
*/functiondataStorageOperator() externalviewreturns (address);
/**
* @notice The contract that deployed the pool, which must adhere to the IAlgebraFactory interface
* @return The contract address
*/functionfactory() externalviewreturns (address);
/**
* @notice The first of the two tokens of the pool, sorted by address
* @return The token contract address
*/functiontoken0() externalviewreturns (address);
/**
* @notice The second of the two tokens of the pool, sorted by address
* @return The token contract address
*/functiontoken1() externalviewreturns (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
*/functiontickSpacing() externalviewreturns (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
*/functionmaxLiquidityPerTick() externalviewreturns (uint128);
}
Contract Source Code
File 14 of 30: IAlgebraPoolPermissionedActions.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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
*/interfaceIAlgebraPoolPermissionedActions{
/**
* @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)
*/functionsetCommunityFee(uint8 communityFee0, uint8 communityFee1) external;
/**
* @notice Sets an active incentive
* @param virtualPoolAddress The address of a virtual pool associated with the incentive
*/functionsetIncentive(address virtualPoolAddress) external;
/**
* @notice Sets new lock time for added liquidity
* @param newLiquidityCooldown The time in seconds
*/functionsetLiquidityCooldown(uint32 newLiquidityCooldown) external;
}
Contract Source Code
File 15 of 30: IAlgebraPoolState.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/interfacesinterfaceIAlgebraPoolState{
/**
* @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;
*/functionglobalState()
externalviewreturns (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
*/functiontotalFeeGrowth0Token() externalviewreturns (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
*/functiontotalFeeGrowth1Token() externalviewreturns (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
*/functionliquidity() externalviewreturns (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.
*/functionticks(int24 tick)
externalviewreturns (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 */functiontickTable(int16 wordPosition) externalviewreturns (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
*/functionpositions(bytes32 key)
externalviewreturns (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;
*/functiontimepoints(uint256 index)
externalviewreturns (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
*/functionactiveIncentive() externalviewreturns (address virtualPool);
/**
* @notice Returns the lock time for added liquidity
*/functionliquidityCooldown() externalviewreturns (uint32 cooldownInSeconds);
}
Contract Source Code
File 16 of 30: IAlgebraSwapCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/interfacesinterfaceIAlgebraSwapCallback{
/// @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 callfunctionalgebraSwapCallback(int256 amount0Delta,
int256 amount1Delta,
bytescalldata data
) external;
}
Contract Source Code
File 17 of 30: IAlgebraVirtualPool.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.5.0;interfaceIAlgebraVirtualPool{
enumStatus {
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
*/functioncross(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
*/functionincreaseCumulative(uint32 currentTimestamp) externalreturns (Status);
}
Contract Source Code
File 18 of 30: IDataStorageOperator.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.5.0;pragmaabicoderv2;import'../libraries/AdaptiveFee.sol';
interfaceIDataStorageOperator{
eventFeeConfiguration(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
*/functiontimepoints(uint256 index)
externalviewreturns (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 tickfunctioninitialize(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`functiongetSingleTimepoint(uint32 time,
uint32 secondsAgo,
int24 tick,
uint16 index,
uint128 liquidity
)
externalviewreturns (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`functiongetTimepoints(uint32 time,
uint32[] memory secondsAgos,
int24 tick,
uint16 index,
uint128 liquidity
)
externalviewreturns (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 rangefunctiongetAverages(uint32 time,
int24 tick,
uint16 index,
uint128 liquidity
) externalviewreturns (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 arrayfunctionwrite(uint16 index,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint128 volumePerLiquidity
) externalreturns (uint16 indexUpdated);
/// @notice Changes fee configuration for the poolfunctionchangeFeeConfiguration(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 << 64functioncalculateVolumePerLiquidity(uint128 liquidity,
int256 amount0,
int256 amount1
) externalpurereturns (uint128 volumePerLiquidity);
/// @return windowLength Length of window used to calculate averagesfunctionwindow() externalviewreturns (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-6functiongetFee(uint32 time,
int24 tick,
uint16 index,
uint128 liquidity
) externalviewreturns (uint16 fee);
}
Contract Source Code
File 19 of 30: IERC20Minimal.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/interfacesinterfaceIERC20Minimal{
/// @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 accountfunctionbalanceOf(address account) externalviewreturns (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 transferfunctiontransfer(address recipient, uint256 amount) externalreturns (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`functionallowance(address owner, address spender) externalviewreturns (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 unsuccessfulfunctionapprove(address spender, uint256 amount) externalreturns (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 unsuccessfulfunctiontransferFrom(address sender,
address recipient,
uint256 amount
) externalreturns (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 transferredeventTransfer(addressindexedfrom, addressindexed 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 spendereventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
Contract Source Code
File 20 of 30: LiquidityMath.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/librarieslibraryLiquidityMath{
/// @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 deltafunctionaddDelta(uint128 x, int128 y) internalpurereturns (uint128 z) {
if (y <0) {
require((z = x -uint128(-y)) < x, 'LS');
} else {
require((z = x +uint128(y)) >= x, 'LA');
}
}
}
Contract Source Code
File 21 of 30: LowGasSafeMath.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/librarieslibraryLowGasSafeMath{
/// @notice Returns x + y, reverts if sum overflows uint256/// @param x The augend/// @param y The addend/// @return z The sum of x and yfunctionadd(uint256 x, uint256 y) internalpurereturns (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 yfunctionsub(uint256 x, uint256 y) internalpurereturns (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 yfunctionmul(uint256 x, uint256 y) internalpurereturns (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 yfunctionadd(int256 x, int256 y) internalpurereturns (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 yfunctionsub(int256 x, int256 y) internalpurereturns (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 yfunctionadd128(uint128 x, uint128 y) internalpurereturns (uint128 z) {
require((z = x + y) >= x);
}
}
// SPDX-License-Identifier: BUSL-1.1pragmasolidity =0.7.6;import'../interfaces/pool/IAlgebraPoolState.sol';
import'../libraries/TickManager.sol';
abstractcontractPoolStateisIAlgebraPoolState{
structGlobalState {
uint160 price; // The square root of the current price in Q64.96 formatint24 tick; // The current tickuint16 fee; // The current fee in hundredths of a bip, i.e. 1e-6uint16 timepointIndex; // The index of the last written timepointuint8 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 IAlgebraPoolStateuint256publicoverride totalFeeGrowth0Token;
/// @inheritdoc IAlgebraPoolStateuint256publicoverride totalFeeGrowth1Token;
/// @inheritdoc IAlgebraPoolState
GlobalState publicoverride globalState;
/// @inheritdoc IAlgebraPoolStateuint128publicoverride liquidity;
uint128internal volumePerLiquidityInBlock;
/// @inheritdoc IAlgebraPoolStateuint32publicoverride liquidityCooldown;
/// @inheritdoc IAlgebraPoolStateaddresspublicoverride activeIncentive;
/// @inheritdoc IAlgebraPoolStatemapping(int24=> TickManager.Tick) publicoverride ticks;
/// @inheritdoc IAlgebraPoolStatemapping(int16=>uint256) publicoverride tickTable;
/// @dev Reentrancy protection. Implemented in every function of the contract since there are checks of balances.modifierlock() {
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 uint32function_blockTimestamp() internalviewvirtualreturns (uint32) {
returnuint32(block.timestamp); // truncation is desired
}
}
Contract Source Code
File 24 of 30: PriceMovementMath.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity =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.libraryPriceMovementMath{
usingLowGasSafeMathforuint256;
usingSafeCastforuint256;
/// @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 token1functiongetNewPriceAfterInput(uint160 price,
uint128 liquidity,
uint256 input,
bool zeroToOne
) internalpurereturns (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 token1functiongetNewPriceAfterOutput(uint160 price,
uint128 liquidity,
uint256 output,
bool zeroToOne
) internalpurereturns (uint160 resultPrice) {
return getNewPrice(price, liquidity, output, zeroToOne, false);
}
functiongetNewPrice(uint160 price,
uint128 liquidity,
uint256 amount,
bool zeroToOne,
bool fromInput
) internalpurereturns (uint160 resultPrice) {
require(price >0);
require(liquidity >0);
if (zeroToOne == fromInput) {
// rounding up or downif (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) returnuint160(FullMath.mulDivRoundingUp(liquidityShifted, price, denominator)); // always fits in 160 bits
}
returnuint160(FullMath.divRoundingUp(liquidityShifted, (liquidityShifted / price).add(amount)));
} else {
uint256 product;
require((product = amount * price) / amount == price); // if the product overflows, we know the denominator underflowsrequire(liquidityShifted > product); // in addition, we must check that the denominator does not underflowreturn 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 inputsif (fromInput) {
returnuint256(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);
returnuint160(price - quotient); // always fits 160 bits
}
}
}
functiongetTokenADelta01(uint160 to,
uint160from,
uint128 liquidity
) internalpurereturns (uint256) {
return TokenDeltaMath.getToken0Delta(to, from, liquidity, true);
}
functiongetTokenADelta10(uint160 to,
uint160from,
uint128 liquidity
) internalpurereturns (uint256) {
return TokenDeltaMath.getToken1Delta(from, to, liquidity, true);
}
functiongetTokenBDelta01(uint160 to,
uint160from,
uint128 liquidity
) internalpurereturns (uint256) {
return TokenDeltaMath.getToken1Delta(to, from, liquidity, false);
}
functiongetTokenBDelta10(uint160 to,
uint160from,
uint128 liquidity
) internalpurereturns (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 feefunctionmovePriceTowardsTarget(bool zeroToOne,
uint160 currentPrice,
uint160 targetPrice,
uint128 liquidity,
int256 amountAvailable,
uint16 fee
)
internalpurereturns (uint160 resultPrice,
uint256 input,
uint256 output,
uint256 feeAmount
)
{
function(uint160, uint160, uint128) purereturns (uint256) getAmountA = zeroToOne ? getTokenADelta01 : getTokenADelta10;
if (amountAvailable >=0) {
// exactIn or notuint256 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) purereturns (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 amountif (output >uint256(amountAvailable)) {
output =uint256(amountAvailable);
}
}
input = getAmountA(resultPrice, currentPrice, liquidity);
feeAmount = FullMath.mulDivRoundingUp(input, fee, 1e6- fee);
}
}
}
Contract Source Code
File 25 of 30: SafeCast.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/librarieslibrarySafeCast{
/// @notice Cast a uint256 to a uint160, revert on overflow/// @param y The uint256 to be downcasted/// @return z The downcasted integer, now type uint160functiontoUint160(uint256 y) internalpurereturns (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 int128functiontoInt128(int256 y) internalpurereturns (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 int256functiontoInt256(uint256 y) internalpurereturns (int256 z) {
require(y <2**255);
z =int256(y);
}
}
Contract Source Code
File 26 of 30: TickManager.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity =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 calculationslibraryTickManager{
usingLowGasSafeMathforint256;
usingSafeCastforint256;
// info stored for each initialized individual tickstructTick {
uint128 liquidityTotal; // the total position liquidity that references this tickint128 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 initializeduint256 outerFeeGrowth0Token;
uint256 outerFeeGrowth1Token;
int56 outerTickCumulative; // the cumulative tick value on the other side of the tickuint160 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 meaningbool 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 boundariesfunctiongetInnerFeeGrowth(mapping(int24 => Tick) storageself,
int24 bottomTick,
int24 topTick,
int24 currentTick,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token
) internalviewreturns (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 versafunctionupdate(mapping(int24 => Tick) storageself,
int24 tick,
int24 currentTick,
int128 liquidityDelta,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token,
uint160 secondsPerLiquidityCumulative,
int56 tickCumulative,
uint32 time,
bool upper
) internalreturns (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 tickif (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)functioncross(mapping(int24 => Tick) storageself,
int24 tick,
uint256 totalFeeGrowth0Token,
uint256 totalFeeGrowth1Token,
uint160 secondsPerLiquidityCumulative,
int56 tickCumulative,
uint32 time
) internalreturns (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;
}
}
Contract Source Code
File 27 of 30: TickMath.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/librarieslibraryTickMath{
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128int24internalconstant MIN_TICK =-887272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128int24internalconstant MAX_TICK =-MIN_TICK;
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)uint160internalconstant MIN_SQRT_RATIO =4295128739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)uint160internalconstant 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 tickfunctiongetSqrtRatioAtTick(int24 tick) internalpurereturns (uint160 price) {
// get abs valueint24 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 ratiofunctiongetTickAtSqrtRatio(uint160 price) internalpurereturns (int24 tick) {
// second inequality must be < because the price can never reach the price at the max tickrequire(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 numberint24 tickLow =int24((log_sqrt10001 -3402992956809132418596140100660247210) >>128);
int24 tickHi =int24((log_sqrt10001 +291339464771989622907027621153398088495) >>128);
tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= price ? tickHi : tickLow;
}
}
Contract Source Code
File 28 of 30: TickTable.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity =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.libraryTickTable{
/// @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 togglefunctiontoggleTick(mapping(int16 => uint256) storageself, int24 tick) internal{
require(tick % Constants.TICK_SPACING ==0, 'tick is not spaced'); // ensure that the tick is spaced
tick /= Constants.TICK_SPACING; // compress tickint16 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-bitfunctiongetSingleSignificantBit(uint256 word) internalpurereturns (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-bitfunctiongetMostSignificantBit(uint256 word) internalpurereturns (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 ticksfunctionnextTickInTheSameRow(mapping(int16 => uint256) storageself,
int24 tick,
bool lte
) internalviewreturns (int24 nextTick, bool initialized) {
{
int24 tickSpacing = Constants.TICK_SPACING;
// compress and round towards negative infinity if negativeassembly {
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 savingsint16 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 bitNumberif (_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 bitNumberuint256 _row =self[rowNumber] >> (bitNumber);
if (_row !=0) {
tick +=int24(getSingleSignificantBit(-_row & _row)); // least significant bitreturn (uncompressAndBoundTick(tick), true);
} else {
tick +=int24(255- bitNumber);
return (uncompressAndBoundTick(tick), false);
}
}
}
functionuncompressAndBoundTick(int24 tick) privatepurereturns (int24 boundedTick) {
boundedTick = tick * Constants.TICK_SPACING;
if (boundedTick < TickMath.MIN_TICK) {
boundedTick = TickMath.MIN_TICK;
} elseif (boundedTick > TickMath.MAX_TICK) {
boundedTick = TickMath.MAX_TICK;
}
}
}
Contract Source Code
File 29 of 30: TokenDeltaMath.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity =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 deltaslibraryTokenDeltaMath{
usingLowGasSafeMathforuint256;
usingSafeCastforuint256;
/// @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 pricesfunctiongetToken0Delta(uint160 priceLower,
uint160 priceUpper,
uint128 liquidity,
bool roundUp
) internalpurereturns (uint256 token0Delta) {
uint256 priceDelta = priceUpper - priceLower;
require(priceDelta < priceUpper); // forbids underflow and 0 priceLoweruint256 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 pricesfunctiongetToken1Delta(uint160 priceLower,
uint160 priceUpper,
uint128 liquidity,
bool roundUp
) internalpurereturns (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 pricesfunctiongetToken0Delta(uint160 priceLower,
uint160 priceUpper,
int128 liquidity
) internalpurereturns (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 pricesfunctiongetToken1Delta(uint160 priceLower,
uint160 priceUpper,
int128 liquidity
) internalpurereturns (int256 token1Delta) {
token1Delta = liquidity >=0
? getToken1Delta(priceLower, priceUpper, uint128(liquidity), true).toInt256()
: -getToken1Delta(priceLower, priceUpper, uint128(-liquidity), false).toInt256();
}
}
Contract Source Code
File 30 of 30: TransferHelper.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=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/librarieslibraryTransferHelper{
/// @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 transferfunctionsafeTransfer(address token,
address to,
uint256 value
) internal{
(bool success, bytesmemory data) = token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value));
require(success && (data.length==0||abi.decode(data, (bool))), 'TF');
}
}