// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {PackedUint128Math} from"./math/PackedUint128Math.sol";
import {Uint256x256Math} from"./math/Uint256x256Math.sol";
import {SafeCast} from"./math/SafeCast.sol";
import {Constants} from"./Constants.sol";
import {PairParameterHelper} from"./PairParameterHelper.sol";
import {FeeHelper} from"./FeeHelper.sol";
import {PriceHelper} from"./PriceHelper.sol";
import {TokenHelper, IERC20} from"./TokenHelper.sol";
/**
* @title Liquidity Book Bin Helper Library
* @author Trader Joe
* @notice This library contains functions to help interaction with bins.
*/libraryBinHelper{
usingPackedUint128Mathforbytes32;
usingPackedUint128Mathforuint128;
usingUint256x256Mathforuint256;
usingPriceHelperforuint24;
usingSafeCastforuint256;
usingPairParameterHelperforbytes32;
usingFeeHelperforuint128;
usingTokenHelperforIERC20;
errorBinHelper__CompositionFactorFlawed(uint24 id);
errorBinHelper__LiquidityOverflow();
errorBinHelper__MaxLiquidityPerBinExceeded();
/**
* @dev Returns the amount of tokens that will be received when burning the given amount of liquidity
* @param binReserves The reserves of the bin
* @param amountToBurn The amount of liquidity to burn
* @param totalSupply The total supply of the liquidity book
* @return amountsOut The encoded amount of tokens that will be received
*/functiongetAmountOutOfBin(bytes32 binReserves, uint256 amountToBurn, uint256 totalSupply)
internalpurereturns (bytes32 amountsOut)
{
(uint128 binReserveX, uint128 binReserveY) = binReserves.decode();
uint128 amountXOutFromBin;
uint128 amountYOutFromBin;
if (binReserveX >0) {
amountXOutFromBin = (amountToBurn.mulDivRoundDown(binReserveX, totalSupply)).safe128();
}
if (binReserveY >0) {
amountYOutFromBin = (amountToBurn.mulDivRoundDown(binReserveY, totalSupply)).safe128();
}
amountsOut = amountXOutFromBin.encode(amountYOutFromBin);
}
/**
* @dev Returns the share and the effective amounts in when adding liquidity
* @param binReserves The reserves of the bin
* @param amountsIn The amounts of tokens to add
* @param price The price of the bin
* @param totalSupply The total supply of the liquidity book
* @return shares The share of the liquidity book that the user will receive
* @return effectiveAmountsIn The encoded effective amounts of tokens that the user will add.
* This is the amount of tokens that the user will actually add to the liquidity book,
* and will always be less than or equal to the amountsIn.
*/functiongetSharesAndEffectiveAmountsIn(bytes32 binReserves, bytes32 amountsIn, uint256 price, uint256 totalSupply)
internalpurereturns (uint256 shares, bytes32 effectiveAmountsIn)
{
(uint256 x, uint256 y) = amountsIn.decode();
uint256 userLiquidity = getLiquidity(x, y, price);
if (userLiquidity ==0) return (0, 0);
uint256 binLiquidity = getLiquidity(binReserves, price);
if (binLiquidity ==0|| totalSupply ==0) return (userLiquidity.sqrt(), amountsIn);
shares = userLiquidity.mulDivRoundDown(totalSupply, binLiquidity);
uint256 effectiveLiquidity = shares.mulDivRoundUp(binLiquidity, totalSupply);
if (userLiquidity > effectiveLiquidity) {
uint256 deltaLiquidity = userLiquidity - effectiveLiquidity;
// The other way might be more efficient, but as y is the quote asset, it is more valuableif (deltaLiquidity >= Constants.SCALE) {
uint256 deltaY = deltaLiquidity >> Constants.SCALE_OFFSET;
deltaY = deltaY > y ? y : deltaY;
y -= deltaY;
deltaLiquidity -= deltaY << Constants.SCALE_OFFSET;
}
if (deltaLiquidity >= price) {
uint256 deltaX = deltaLiquidity / price;
deltaX = deltaX > x ? x : deltaX;
x -= deltaX;
}
amountsIn =uint128(x).encode(uint128(y));
}
if (getLiquidity(binReserves.add(amountsIn), price) > Constants.MAX_LIQUIDITY_PER_BIN) {
revert BinHelper__MaxLiquidityPerBinExceeded();
}
return (shares, amountsIn);
}
/**
* @dev Returns the amount of liquidity following the constant sum formula `L = price * x + y`
* @param amounts The amounts of tokens
* @param price The price of the bin
* @return liquidity The amount of liquidity
*/functiongetLiquidity(bytes32 amounts, uint256 price) internalpurereturns (uint256 liquidity) {
(uint256 x, uint256 y) = amounts.decode();
return getLiquidity(x, y, price);
}
/**
* @dev Returns the amount of liquidity following the constant sum formula `L = price * x + y`
* @param x The amount of the token X
* @param y The amount of the token Y
* @param price The price of the bin
* @return liquidity The amount of liquidity
*/functiongetLiquidity(uint256 x, uint256 y, uint256 price) internalpurereturns (uint256 liquidity) {
if (x >0) {
unchecked {
liquidity = price * x;
if (liquidity / x != price) revert BinHelper__LiquidityOverflow();
}
}
if (y >0) {
unchecked {
y <<= Constants.SCALE_OFFSET;
liquidity += y;
if (liquidity < y) revert BinHelper__LiquidityOverflow();
}
}
return liquidity;
}
/**
* @dev Verify that the amounts are correct and that the composition factor is not flawed
* @param amounts The amounts of tokens
* @param activeId The id of the active bin
* @param id The id of the bin
*/functionverifyAmounts(bytes32 amounts, uint24 activeId, uint24 id) internalpure{
if (id < activeId && (amounts <<128) >0|| id > activeId &&uint256(amounts) >type(uint128).max) {
revert BinHelper__CompositionFactorFlawed(id);
}
}
/**
* @dev Returns the composition fees when adding liquidity to the active bin with a different
* composition factor than the bin's one, as it does an implicit swap
* @param binReserves The reserves of the bin
* @param parameters The parameters of the liquidity book
* @param binStep The step of the bin
* @param amountsIn The amounts of tokens to add
* @param totalSupply The total supply of the liquidity book
* @param shares The share of the liquidity book that the user will receive
* @return fees The encoded fees that will be charged
*/functiongetCompositionFees(bytes32 binReserves,
bytes32 parameters,
uint16 binStep,
bytes32 amountsIn,
uint256 totalSupply,
uint256 shares
) internalpurereturns (bytes32 fees) {
if (shares ==0) return0;
(uint128 amountX, uint128 amountY) = amountsIn.decode();
(uint128 receivedAmountX, uint128 receivedAmountY) =
getAmountOutOfBin(binReserves.add(amountsIn), shares, totalSupply + shares).decode();
if (receivedAmountX > amountX) {
uint128 feeY = (amountY - receivedAmountY).getCompositionFee(parameters.getTotalFee(binStep));
fees = feeY.encodeSecond();
} elseif (receivedAmountY > amountY) {
uint128 feeX = (amountX - receivedAmountX).getCompositionFee(parameters.getTotalFee(binStep));
fees = feeX.encodeFirst();
}
}
/**
* @dev Returns whether the bin is empty (true) or not (false)
* @param binReserves The reserves of the bin
* @param isX Whether the reserve to check is the X reserve (true) or the Y reserve (false)
* @return Whether the bin is empty (true) or not (false)
*/functionisEmpty(bytes32 binReserves, bool isX) internalpurereturns (bool) {
return isX ? binReserves.decodeX() ==0 : binReserves.decodeY() ==0;
}
/**
* @dev Returns the amounts of tokens that will be added and removed from the bin during a swap
* along with the fees that will be charged
* @param binReserves The reserves of the bin
* @param parameters The parameters of the liquidity book
* @param binStep The step of the bin
* @param swapForY Whether the swap is for Y (true) or for X (false)
* @param activeId The id of the active bin
* @param amountsInLeft The amounts of tokens left to swap
* @return amountsInWithFees The encoded amounts of tokens that will be added to the bin, including fees
* @return amountsOutOfBin The encoded amounts of tokens that will be removed from the bin
* @return totalFees The encoded fees that will be charged
*/functiongetAmounts(bytes32 binReserves,
bytes32 parameters,
uint16 binStep,
bool swapForY, // swap `swapForY` and `activeId` to avoid stack too deepuint24 activeId,
bytes32 amountsInLeft
) internalpurereturns (bytes32 amountsInWithFees, bytes32 amountsOutOfBin, bytes32 totalFees) {
uint256 price = activeId.getPriceFromId(binStep);
{
uint128 binReserveOut = binReserves.decode(!swapForY);
uint128 maxAmountIn = swapForY
? uint256(binReserveOut).shiftDivRoundUp(Constants.SCALE_OFFSET, price).safe128()
: uint256(binReserveOut).mulShiftRoundUp(price, Constants.SCALE_OFFSET).safe128();
uint128 totalFee = parameters.getTotalFee(binStep);
uint128 maxFee = maxAmountIn.getFeeAmount(totalFee);
maxAmountIn += maxFee;
uint128 amountIn128 = amountsInLeft.decode(swapForY);
uint128 fee128;
uint128 amountOut128;
if (amountIn128 >= maxAmountIn) {
fee128 = maxFee;
amountIn128 = maxAmountIn;
amountOut128 = binReserveOut;
} else {
fee128 = amountIn128.getFeeAmountFrom(totalFee);
uint256 amountIn = amountIn128 - fee128;
amountOut128 = swapForY
? uint256(amountIn).mulShiftRoundDown(price, Constants.SCALE_OFFSET).safe128()
: uint256(amountIn).shiftDivRoundDown(Constants.SCALE_OFFSET, price).safe128();
if (amountOut128 > binReserveOut) amountOut128 = binReserveOut;
}
(amountsInWithFees, amountsOutOfBin, totalFees) = swapForY
? (amountIn128.encodeFirst(), amountOut128.encodeSecond(), fee128.encodeFirst())
: (amountIn128.encodeSecond(), amountOut128.encodeFirst(), fee128.encodeSecond());
}
if (
getLiquidity(binReserves.add(amountsInWithFees).sub(amountsOutOfBin), price)
> Constants.MAX_LIQUIDITY_PER_BIN
) {
revert BinHelper__MaxLiquidityPerBinExceeded();
}
}
/**
* @dev Returns the encoded amounts that were transferred to the contract
* @param reserves The reserves
* @param tokenX The token X
* @param tokenY The token Y
* @return amounts The amounts, encoded as follows:
* [0 - 128[: amountX
* [128 - 256[: amountY
*/functionreceived(bytes32 reserves, IERC20 tokenX, IERC20 tokenY) internalviewreturns (bytes32 amounts) {
amounts = _balanceOf(tokenX).encode(_balanceOf(tokenY)).sub(reserves);
}
/**
* @dev Returns the encoded amounts that were transferred to the contract, only for token X
* @param reserves The reserves
* @param tokenX The token X
* @return amounts The amounts, encoded as follows:
* [0 - 128[: amountX
* [128 - 256[: empty
*/functionreceivedX(bytes32 reserves, IERC20 tokenX) internalviewreturns (bytes32) {
uint128 reserveX = reserves.decodeX();
return (_balanceOf(tokenX) - reserveX).encodeFirst();
}
/**
* @dev Returns the encoded amounts that were transferred to the contract, only for token Y
* @param reserves The reserves
* @param tokenY The token Y
* @return amounts The amounts, encoded as follows:
* [0 - 128[: empty
* [128 - 256[: amountY
*/functionreceivedY(bytes32 reserves, IERC20 tokenY) internalviewreturns (bytes32) {
uint128 reserveY = reserves.decodeY();
return (_balanceOf(tokenY) - reserveY).encodeSecond();
}
/**
* @dev Transfers the encoded amounts to the recipient
* @param amounts The amounts, encoded as follows:
* [0 - 128[: amountX
* [128 - 256[: amountY
* @param tokenX The token X
* @param tokenY The token Y
* @param recipient The recipient
*/functiontransfer(bytes32 amounts, IERC20 tokenX, IERC20 tokenY, address recipient) internal{
(uint128 amountX, uint128 amountY) = amounts.decode();
if (amountX >0) tokenX.safeTransfer(recipient, amountX);
if (amountY >0) tokenY.safeTransfer(recipient, amountY);
}
/**
* @dev Transfers the encoded amounts to the recipient, only for token X
* @param amounts The amounts, encoded as follows:
* [0 - 128[: amountX
* [128 - 256[: empty
* @param tokenX The token X
* @param recipient The recipient
*/functiontransferX(bytes32 amounts, IERC20 tokenX, address recipient) internal{
uint128 amountX = amounts.decodeX();
if (amountX >0) tokenX.safeTransfer(recipient, amountX);
}
/**
* @dev Transfers the encoded amounts to the recipient, only for token Y
* @param amounts The amounts, encoded as follows:
* [0 - 128[: empty
* [128 - 256[: amountY
* @param tokenY The token Y
* @param recipient The recipient
*/functiontransferY(bytes32 amounts, IERC20 tokenY, address recipient) internal{
uint128 amountY = amounts.decodeY();
if (amountY >0) tokenY.safeTransfer(recipient, amountY);
}
function_balanceOf(IERC20 token) privateviewreturns (uint128) {
return token.balanceOf(address(this)).safe128();
}
}
Contract Source Code
File 2 of 33: BitMath.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;/**
* @title Liquidity Book Bit Math Library
* @author Trader Joe
* @notice Helper contract used for bit calculations
*/libraryBitMath{
/**
* @dev Returns the index of the closest bit on the right of x that is non null
* @param x The value as a uint256
* @param bit The index of the bit to start searching at
* @return id The index of the closest non null bit on the right of x.
* If there is no closest bit, it returns max(uint256)
*/functionclosestBitRight(uint256 x, uint8 bit) internalpurereturns (uint256 id) {
unchecked {
uint256 shift =255- bit;
x <<= shift;
// can't overflow as it's non-zero and we shifted it by `_shift`return (x ==0) ? type(uint256).max : mostSignificantBit(x) - shift;
}
}
/**
* @dev Returns the index of the closest bit on the left of x that is non null
* @param x The value as a uint256
* @param bit The index of the bit to start searching at
* @return id The index of the closest non null bit on the left of x.
* If there is no closest bit, it returns max(uint256)
*/functionclosestBitLeft(uint256 x, uint8 bit) internalpurereturns (uint256 id) {
unchecked {
x >>= bit;
return (x ==0) ? type(uint256).max : leastSignificantBit(x) + bit;
}
}
/**
* @dev Returns the index of the most significant bit of x
* This function returns 0 if x is 0
* @param x The value as a uint256
* @return msb The index of the most significant bit of x
*/functionmostSignificantBit(uint256 x) internalpurereturns (uint8 msb) {
assembly {
ifgt(x, 0xffffffffffffffffffffffffffffffff) {
x :=shr(128, x)
msb :=128
}
ifgt(x, 0xffffffffffffffff) {
x :=shr(64, x)
msb :=add(msb, 64)
}
ifgt(x, 0xffffffff) {
x :=shr(32, x)
msb :=add(msb, 32)
}
ifgt(x, 0xffff) {
x :=shr(16, x)
msb :=add(msb, 16)
}
ifgt(x, 0xff) {
x :=shr(8, x)
msb :=add(msb, 8)
}
ifgt(x, 0xf) {
x :=shr(4, x)
msb :=add(msb, 4)
}
ifgt(x, 0x3) {
x :=shr(2, x)
msb :=add(msb, 2)
}
ifgt(x, 0x1) { msb :=add(msb, 1) }
}
}
/**
* @dev Returns the index of the least significant bit of x
* This function returns 255 if x is 0
* @param x The value as a uint256
* @return lsb The index of the least significant bit of x
*/functionleastSignificantBit(uint256 x) internalpurereturns (uint8 lsb) {
assembly {
let sx :=shl(128, x)
ifiszero(iszero(sx)) {
lsb :=128
x := sx
}
sx :=shl(64, x)
ifiszero(iszero(sx)) {
x := sx
lsb :=add(lsb, 64)
}
sx :=shl(32, x)
ifiszero(iszero(sx)) {
x := sx
lsb :=add(lsb, 32)
}
sx :=shl(16, x)
ifiszero(iszero(sx)) {
x := sx
lsb :=add(lsb, 16)
}
sx :=shl(8, x)
ifiszero(iszero(sx)) {
x := sx
lsb :=add(lsb, 8)
}
sx :=shl(4, x)
ifiszero(iszero(sx)) {
x := sx
lsb :=add(lsb, 4)
}
sx :=shl(2, x)
ifiszero(iszero(sx)) {
x := sx
lsb :=add(lsb, 2)
}
ifiszero(iszero(shl(1, x))) { lsb :=add(lsb, 1) }
lsb :=sub(255, lsb)
}
}
}
Contract Source Code
File 3 of 33: Constants.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;/**
* @title Liquidity Book Constants Library
* @author Trader Joe
* @notice Set of constants for Liquidity Book contracts
*/libraryConstants{
uint8internalconstant SCALE_OFFSET =128;
uint256internalconstant SCALE =1<< SCALE_OFFSET;
uint256internalconstant PRECISION =1e18;
uint256internalconstant SQUARED_PRECISION = PRECISION * PRECISION;
uint256internalconstant MAX_FEE =0.1e18; // 10%uint256internalconstant MAX_PROTOCOL_SHARE =2_500; // 25% of the feeuint256internalconstant BASIS_POINT_MAX =10_000;
// (2^256 - 1) / (2 * log(2**128) / log(1.0001))uint256internalconstant MIN_LIQUIDITY_PER_BIN =1000;
uint256internalconstant MAX_LIQUIDITY_PER_BIN =65251743116719673010965625540244653191619923014385985379600384103134737;
/// @dev The expected return after a successful flash loanbytes32internalconstant CALLBACK_SUCCESS =keccak256("LBPair.onFlashLoan");
}
Contract Source Code
File 4 of 33: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)pragmasolidity ^0.8.20;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
function_contextSuffixLength() internalviewvirtualreturns (uint256) {
return0;
}
}
Contract Source Code
File 5 of 33: Encoded.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;/**
* @title Liquidity Book Encoded Library
* @author Trader Joe
* @notice Helper contract used for decoding bytes32 sample
*/libraryEncoded{
uint256internalconstant MASK_UINT1 =0x1;
uint256internalconstant MASK_UINT8 =0xff;
uint256internalconstant MASK_UINT12 =0xfff;
uint256internalconstant MASK_UINT14 =0x3fff;
uint256internalconstant MASK_UINT16 =0xffff;
uint256internalconstant MASK_UINT20 =0xfffff;
uint256internalconstant MASK_UINT24 =0xffffff;
uint256internalconstant MASK_UINT40 =0xffffffffff;
uint256internalconstant MASK_UINT64 =0xffffffffffffffff;
uint256internalconstant MASK_UINT128 =0xffffffffffffffffffffffffffffffff;
/**
* @notice Internal function to set a value in an encoded bytes32 using a mask and offset
* @dev This function can overflow
* @param encoded The previous encoded value
* @param value The value to encode
* @param mask The mask
* @param offset The offset
* @return newEncoded The new encoded value
*/functionset(bytes32 encoded, uint256 value, uint256 mask, uint256 offset)
internalpurereturns (bytes32 newEncoded)
{
assembly {
newEncoded :=and(encoded, not(shl(offset, mask)))
newEncoded :=or(newEncoded, shl(offset, and(value, mask)))
}
}
/**
* @notice Internal function to set a bool in an encoded bytes32 using an offset
* @dev This function can overflow
* @param encoded The previous encoded value
* @param boolean The bool to encode
* @param offset The offset
* @return newEncoded The new encoded value
*/functionsetBool(bytes32 encoded, bool boolean, uint256 offset) internalpurereturns (bytes32 newEncoded) {
return set(encoded, boolean ? 1 : 0, MASK_UINT1, offset);
}
/**
* @notice Internal function to decode a bytes32 sample using a mask and offset
* @dev This function can overflow
* @param encoded The encoded value
* @param mask The mask
* @param offset The offset
* @return value The decoded value
*/functiondecode(bytes32 encoded, uint256 mask, uint256 offset) internalpurereturns (uint256 value) {
assembly {
value :=and(shr(offset, encoded), mask)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a bool using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return boolean The decoded value as a bool
*/functiondecodeBool(bytes32 encoded, uint256 offset) internalpurereturns (bool boolean) {
assembly {
boolean :=and(shr(offset, encoded), MASK_UINT1)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint8 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value
*/functiondecodeUint8(bytes32 encoded, uint256 offset) internalpurereturns (uint8 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT8)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint12 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value as a uint16, since uint12 is not supported
*/functiondecodeUint12(bytes32 encoded, uint256 offset) internalpurereturns (uint16 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT12)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint14 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value as a uint16, since uint14 is not supported
*/functiondecodeUint14(bytes32 encoded, uint256 offset) internalpurereturns (uint16 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT14)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint16 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value
*/functiondecodeUint16(bytes32 encoded, uint256 offset) internalpurereturns (uint16 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT16)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint20 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value as a uint24, since uint20 is not supported
*/functiondecodeUint20(bytes32 encoded, uint256 offset) internalpurereturns (uint24 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT20)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint24 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value
*/functiondecodeUint24(bytes32 encoded, uint256 offset) internalpurereturns (uint24 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT24)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint40 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value
*/functiondecodeUint40(bytes32 encoded, uint256 offset) internalpurereturns (uint40 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT40)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint64 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value
*/functiondecodeUint64(bytes32 encoded, uint256 offset) internalpurereturns (uint64 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT64)
}
}
/**
* @notice Internal function to decode a bytes32 sample into a uint128 using an offset
* @dev This function can overflow
* @param encoded The encoded value
* @param offset The offset
* @return value The decoded value
*/functiondecodeUint128(bytes32 encoded, uint256 offset) internalpurereturns (uint128 value) {
assembly {
value :=and(shr(offset, encoded), MASK_UINT128)
}
}
}
Contract Source Code
File 6 of 33: FeeHelper.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {Constants} from"./Constants.sol";
/**
* @title Liquidity Book Fee Helper Library
* @author Trader Joe
* @notice This library contains functions to calculate fees
*/libraryFeeHelper{
errorFeeHelper__FeeTooLarge();
errorFeeHelper__ProtocolShareTooLarge();
/**
* @dev Modifier to check that the fee is not too large
* @param fee The fee
*/modifierverifyFee(uint128 fee) {
_verifyFee(fee);
_;
}
/**
* @dev Modifier to check that the protocol share is not too large
* @param protocolShare The protocol share
*/modifierverifyProtocolShare(uint128 protocolShare) {
if (protocolShare > Constants.MAX_PROTOCOL_SHARE) revert FeeHelper__ProtocolShareTooLarge();
_;
}
/**
* @dev Calculates the fee amount from the amount with fees, rounding up
* @param amountWithFees The amount with fees
* @param totalFee The total fee
* @return feeAmount The fee amount
*/functiongetFeeAmountFrom(uint128 amountWithFees, uint128 totalFee)
internalpureverifyFee(totalFee)
returns (uint128)
{
unchecked {
// Can't overflow, max(result) = (type(uint128).max * 0.1e18 + 1e18 - 1) / 1e18 < 2^128returnuint128((uint256(amountWithFees) * totalFee + Constants.PRECISION -1) / Constants.PRECISION);
}
}
/**
* @dev Calculates the fee amount that will be charged, rounding up
* @param amount The amount
* @param totalFee The total fee
* @return feeAmount The fee amount
*/functiongetFeeAmount(uint128 amount, uint128 totalFee) internalpureverifyFee(totalFee) returns (uint128) {
unchecked {
uint256 denominator = Constants.PRECISION - totalFee;
// Can't overflow, max(result) = (type(uint128).max * 0.1e18 + (1e18 - 1)) / 0.9e18 < 2^128returnuint128((uint256(amount) * totalFee + denominator -1) / denominator);
}
}
/**
* @dev Calculates the composition fee amount from the amount with fees, rounding down
* @param amountWithFees The amount with fees
* @param totalFee The total fee
* @return The amount with fees
*/functiongetCompositionFee(uint128 amountWithFees, uint128 totalFee)
internalpureverifyFee(totalFee)
returns (uint128)
{
unchecked {
uint256 denominator = Constants.SQUARED_PRECISION;
// Can't overflow, max(result) = type(uint128).max * 0.1e18 * 1.1e18 / 1e36 <= 2^128 * 0.11e36 / 1e36 < 2^128returnuint128(uint256(amountWithFees) * totalFee * (uint256(totalFee) + Constants.PRECISION) / denominator);
}
}
/**
* @dev Calculates the protocol fee amount from the fee amount and the protocol share, rounding down
* @param feeAmount The fee amount
* @param protocolShare The protocol share
* @return protocolFeeAmount The protocol fee amount
*/functiongetProtocolFeeAmount(uint128 feeAmount, uint128 protocolShare)
internalpureverifyProtocolShare(protocolShare)
returns (uint128)
{
unchecked {
returnuint128(uint256(feeAmount) * protocolShare / Constants.BASIS_POINT_MAX);
}
}
/**
* @dev Internal function to check that the fee is not too large
* @param fee The fee
*/function_verifyFee(uint128 fee) privatepure{
if (fee > Constants.MAX_FEE) revert FeeHelper__FeeTooLarge();
}
}
Contract Source Code
File 7 of 33: Hooks.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {ILBHooks} from"../interfaces/ILBHooks.sol";
/**
* @title Hooks library
* @notice This library contains functions that should be used to interact with hooks
*/libraryHooks{
errorHooks__CallFailed();
bytes32internalconstant BEFORE_SWAP_FLAG =bytes32(uint256(1<<160));
bytes32internalconstant AFTER_SWAP_FLAG =bytes32(uint256(1<<161));
bytes32internalconstant BEFORE_FLASH_LOAN_FLAG =bytes32(uint256(1<<162));
bytes32internalconstant AFTER_FLASH_LOAN_FLAG =bytes32(uint256(1<<163));
bytes32internalconstant BEFORE_MINT_FLAG =bytes32(uint256(1<<164));
bytes32internalconstant AFTER_MINT_FLAG =bytes32(uint256(1<<165));
bytes32internalconstant BEFORE_BURN_FLAG =bytes32(uint256(1<<166));
bytes32internalconstant AFTER_BURN_FLAG =bytes32(uint256(1<<167));
bytes32internalconstant BEFORE_TRANSFER_FLAG =bytes32(uint256(1<<168));
bytes32internalconstant AFTER_TRANSFER_FLAG =bytes32(uint256(1<<169));
structParameters {
address hooks;
bool beforeSwap;
bool afterSwap;
bool beforeFlashLoan;
bool afterFlashLoan;
bool beforeMint;
bool afterMint;
bool beforeBurn;
bool afterBurn;
bool beforeBatchTransferFrom;
bool afterBatchTransferFrom;
}
/**
* @dev Helper function to encode the hooks parameters to a single bytes32 value
* @param parameters The hooks parameters
* @return hooksParameters The encoded hooks parameters
*/functionencode(Parameters memory parameters) internalpurereturns (bytes32 hooksParameters) {
hooksParameters =bytes32(uint256(uint160(address(parameters.hooks))));
if (parameters.beforeSwap) hooksParameters |= BEFORE_SWAP_FLAG;
if (parameters.afterSwap) hooksParameters |= AFTER_SWAP_FLAG;
if (parameters.beforeFlashLoan) hooksParameters |= BEFORE_FLASH_LOAN_FLAG;
if (parameters.afterFlashLoan) hooksParameters |= AFTER_FLASH_LOAN_FLAG;
if (parameters.beforeMint) hooksParameters |= BEFORE_MINT_FLAG;
if (parameters.afterMint) hooksParameters |= AFTER_MINT_FLAG;
if (parameters.beforeBurn) hooksParameters |= BEFORE_BURN_FLAG;
if (parameters.afterBurn) hooksParameters |= AFTER_BURN_FLAG;
if (parameters.beforeBatchTransferFrom) hooksParameters |= BEFORE_TRANSFER_FLAG;
if (parameters.afterBatchTransferFrom) hooksParameters |= AFTER_TRANSFER_FLAG;
}
/**
* @dev Helper function to decode the hooks parameters from a single bytes32 value
* @param hooksParameters The encoded hooks parameters
* @return parameters The hooks parameters
*/functiondecode(bytes32 hooksParameters) internalpurereturns (Parameters memory parameters) {
parameters.hooks = getHooks(hooksParameters);
parameters.beforeSwap = (hooksParameters & BEFORE_SWAP_FLAG) !=0;
parameters.afterSwap = (hooksParameters & AFTER_SWAP_FLAG) !=0;
parameters.beforeFlashLoan = (hooksParameters & BEFORE_FLASH_LOAN_FLAG) !=0;
parameters.afterFlashLoan = (hooksParameters & AFTER_FLASH_LOAN_FLAG) !=0;
parameters.beforeMint = (hooksParameters & BEFORE_MINT_FLAG) !=0;
parameters.afterMint = (hooksParameters & AFTER_MINT_FLAG) !=0;
parameters.beforeBurn = (hooksParameters & BEFORE_BURN_FLAG) !=0;
parameters.afterBurn = (hooksParameters & AFTER_BURN_FLAG) !=0;
parameters.beforeBatchTransferFrom = (hooksParameters & BEFORE_TRANSFER_FLAG) !=0;
parameters.afterBatchTransferFrom = (hooksParameters & AFTER_TRANSFER_FLAG) !=0;
}
/**
* @dev Helper function to get the hooks address from the encoded hooks parameters
* @param hooksParameters The encoded hooks parameters
* @return hooks The hooks address
*/functiongetHooks(bytes32 hooksParameters) internalpurereturns (address hooks) {
hooks =address(uint160(uint256(hooksParameters)));
}
/**
* @dev Helper function to set the hooks address in the encoded hooks parameters
* @param hooksParameters The encoded hooks parameters
* @param newHooks The new hooks address
* @return hooksParameters The updated hooks parameters
*/functionsetHooks(bytes32 hooksParameters, address newHooks) internalpurereturns (bytes32) {
returnbytes32(bytes12(hooksParameters)) |bytes32(uint256(uint160(newHooks)));
}
/**
* @dev Helper function to get the flags from the encoded hooks parameters
* @param hooksParameters The encoded hooks parameters
* @return flags The flags
*/functiongetFlags(bytes32 hooksParameters) internalpurereturns (bytes12 flags) {
flags =bytes12(hooksParameters);
}
/**
* @dev Helper function call the onHooksSet function on the hooks contract, only if the
* hooksParameters is not 0
* @param hooksParameters The encoded hooks parameters
* @param onHooksSetData The data to pass to the onHooksSet function
*/functiononHooksSet(bytes32 hooksParameters, bytescalldata onHooksSetData) internal{
if (hooksParameters !=0) {
_safeCall(
hooksParameters, abi.encodeWithSelector(ILBHooks.onHooksSet.selector, hooksParameters, onHooksSetData)
);
}
}
/**
* @dev Helper function to call the beforeSwap function on the hooks contract, only if the
* BEFORE_SWAP_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param to The recipient
* @param swapForY Whether the swap is for Y
* @param amountsIn The amounts in
*/functionbeforeSwap(bytes32 hooksParameters, address sender, address to, bool swapForY, bytes32 amountsIn)
internal{
if ((hooksParameters & BEFORE_SWAP_FLAG) !=0) {
_safeCall(
hooksParameters, abi.encodeWithSelector(ILBHooks.beforeSwap.selector, sender, to, swapForY, amountsIn)
);
}
}
/**
* @dev Helper function to call the afterSwap function on the hooks contract, only if the
* AFTER_SWAP_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param to The recipient
* @param swapForY Whether the swap is for Y
* @param amountsOut The amounts out
*/functionafterSwap(bytes32 hooksParameters, address sender, address to, bool swapForY, bytes32 amountsOut)
internal{
if ((hooksParameters & AFTER_SWAP_FLAG) !=0) {
_safeCall(
hooksParameters, abi.encodeWithSelector(ILBHooks.afterSwap.selector, sender, to, swapForY, amountsOut)
);
}
}
/**
* @dev Helper function to call the beforeFlashLoan function on the hooks contract, only if the
* BEFORE_FLASH_LOAN_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param to The recipient
* @param amounts The amounts
*/functionbeforeFlashLoan(bytes32 hooksParameters, address sender, address to, bytes32 amounts) internal{
if ((hooksParameters & BEFORE_FLASH_LOAN_FLAG) !=0) {
_safeCall(hooksParameters, abi.encodeWithSelector(ILBHooks.beforeFlashLoan.selector, sender, to, amounts));
}
}
/**
* @dev Helper function to call the afterFlashLoan function on the hooks contract, only if the
* AFTER_FLASH_LOAN_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param to The recipient
* @param fees The fees
* @param feesReceived The fees received
*/functionafterFlashLoan(bytes32 hooksParameters, address sender, address to, bytes32 fees, bytes32 feesReceived)
internal{
if ((hooksParameters & AFTER_FLASH_LOAN_FLAG) !=0) {
_safeCall(
hooksParameters,
abi.encodeWithSelector(ILBHooks.afterFlashLoan.selector, sender, to, fees, feesReceived)
);
}
}
/**
* @dev Helper function to call the beforeMint function on the hooks contract, only if the
* BEFORE_MINT_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param to The recipient
* @param liquidityConfigs The liquidity configs
* @param amountsReceived The amounts received
*/functionbeforeMint(bytes32 hooksParameters,
address sender,
address to,
bytes32[] calldata liquidityConfigs,
bytes32 amountsReceived
) internal{
if ((hooksParameters & BEFORE_MINT_FLAG) !=0) {
_safeCall(
hooksParameters,
abi.encodeWithSelector(ILBHooks.beforeMint.selector, sender, to, liquidityConfigs, amountsReceived)
);
}
}
/**
* @dev Helper function to call the afterMint function on the hooks contract, only if the
* AFTER_MINT_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param to The recipient
* @param liquidityConfigs The liquidity configs
* @param amountsIn The amounts in
*/functionafterMint(bytes32 hooksParameters,
address sender,
address to,
bytes32[] calldata liquidityConfigs,
bytes32 amountsIn
) internal{
if ((hooksParameters & AFTER_MINT_FLAG) !=0) {
_safeCall(
hooksParameters,
abi.encodeWithSelector(ILBHooks.afterMint.selector, sender, to, liquidityConfigs, amountsIn)
);
}
}
/**
* @dev Helper function to call the beforeBurn function on the hooks contract, only if the
* BEFORE_BURN_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param from The sender
* @param to The recipient
* @param ids The ids
* @param amountsToBurn The amounts to burn
*/functionbeforeBurn(bytes32 hooksParameters,
address sender,
addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amountsToBurn
) internal{
if ((hooksParameters & BEFORE_BURN_FLAG) !=0) {
_safeCall(
hooksParameters,
abi.encodeWithSelector(ILBHooks.beforeBurn.selector, sender, from, to, ids, amountsToBurn)
);
}
}
/**
* @dev Helper function to call the afterBurn function on the hooks contract, only if the
* AFTER_BURN_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param from The sender
* @param to The recipient
* @param ids The ids
* @param amountsToBurn The amounts to burn
*/functionafterBurn(bytes32 hooksParameters,
address sender,
addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amountsToBurn
) internal{
if ((hooksParameters & AFTER_BURN_FLAG) !=0) {
_safeCall(
hooksParameters,
abi.encodeWithSelector(ILBHooks.afterBurn.selector, sender, from, to, ids, amountsToBurn)
);
}
}
/**
* @dev Helper function to call the beforeTransferFrom function on the hooks contract, only if the
* BEFORE_TRANSFER_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param from The sender
* @param to The recipient
* @param ids The list of ids
* @param amounts The list of amounts
*/functionbeforeBatchTransferFrom(bytes32 hooksParameters,
address sender,
addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts
) internal{
if ((hooksParameters & BEFORE_TRANSFER_FLAG) !=0) {
_safeCall(
hooksParameters,
abi.encodeWithSelector(ILBHooks.beforeBatchTransferFrom.selector, sender, from, to, ids, amounts)
);
}
}
/**
* @dev Helper function to call the afterTransferFrom function on the hooks contract, only if the
* AFTER_TRANSFER_FLAG is set in the hooksParameters
* @param hooksParameters The encoded hooks parameters
* @param sender The sender
* @param from The sender
* @param to The recipient
* @param ids The list of ids
* @param amounts The list of amounts
*/functionafterBatchTransferFrom(bytes32 hooksParameters,
address sender,
addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts
) internal{
if ((hooksParameters & AFTER_TRANSFER_FLAG) !=0) {
_safeCall(
hooksParameters,
abi.encodeWithSelector(ILBHooks.afterBatchTransferFrom.selector, sender, from, to, ids, amounts)
);
}
}
/**
* @dev Helper function to call the hooks contract and verify the call was successful
* by matching the expected selector with the returned data
* @param hooksParameters The encoded hooks parameters
* @param data The data to pass to the hooks contract
*/function_safeCall(bytes32 hooksParameters, bytesmemory data) private{
bool success;
address hooks = getHooks(hooksParameters);
assembly {
let expectedSelector :=shr(224, mload(add(data, 0x20)))
success :=call(gas(), hooks, 0, add(data, 0x20), mload(data), 0, 0x20)
ifand(iszero(success), iszero(iszero(returndatasize()))) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
success :=and(success, and(gt(returndatasize(), 0x1f), eq(shr(224, mload(0)), expectedSelector)))
}
if (!success) revert Hooks__CallFailed();
}
}
Contract Source Code
File 8 of 33: IERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)pragmasolidity ^0.8.20;/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/interfaceIERC165{
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 9 of 33: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.20;/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 value) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 value) externalreturns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom, address to, uint256 value) externalreturns (bool);
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {IERC20} from"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ILBLegacyToken} from"./ILBLegacyToken.sol";
/// @title Liquidity Book Pair V2 Interface/// @author Trader Joe/// @notice Required interface of LBPair contractinterfaceILBLegacyPairisILBLegacyToken{
/// @dev Structure to store the protocol fees:/// - binStep: The bin step/// - baseFactor: The base factor/// - filterPeriod: The filter period, where the fees stays constant/// - decayPeriod: The decay period, where the fees are halved/// - reductionFactor: The reduction factor, used to calculate the reduction of the accumulator/// - variableFeeControl: The variable fee control, used to control the variable fee, can be 0 to disable them/// - protocolShare: The share of fees sent to protocol/// - maxVolatilityAccumulated: The max value of volatility accumulated/// - volatilityAccumulated: The value of volatility accumulated/// - volatilityReference: The value of volatility reference/// - indexRef: The index reference/// - time: The last time the accumulator was calledstructFeeParameters {
// 144 lowest bits in slotuint16 binStep;
uint16 baseFactor;
uint16 filterPeriod;
uint16 decayPeriod;
uint16 reductionFactor;
uint24 variableFeeControl;
uint16 protocolShare;
uint24 maxVolatilityAccumulated;
// 112 highest bits in slotuint24 volatilityAccumulated;
uint24 volatilityReference;
uint24 indexRef;
uint40 time;
}
/// @dev Structure used during swaps to distributes the fees:/// - total: The total amount of fees/// - protocol: The amount of fees reserved for protocolstructFeesDistribution {
uint128 total;
uint128 protocol;
}
/// @dev Structure to store the reserves of bins:/// - reserveX: The current reserve of tokenX of the bin/// - reserveY: The current reserve of tokenY of the binstructBin {
uint112 reserveX;
uint112 reserveY;
uint256 accTokenXPerShare;
uint256 accTokenYPerShare;
}
/// @dev Structure to store the information of the pair such as:/// slot0:/// - activeId: The current id used for swaps, this is also linked with the price/// - reserveX: The sum of amounts of tokenX across all bins/// slot1:/// - reserveY: The sum of amounts of tokenY across all bins/// - oracleSampleLifetime: The lifetime of an oracle sample/// - oracleSize: The current size of the oracle, can be increase by users/// - oracleActiveSize: The current active size of the oracle, composed only from non empty data sample/// - oracleLastTimestamp: The current last timestamp at which a sample was added to the circular buffer/// - oracleId: The current id of the oracle/// slot2:/// - feesX: The current amount of fees to distribute in tokenX (total, protocol)/// slot3:/// - feesY: The current amount of fees to distribute in tokenY (total, protocol)structPairInformation {
uint24 activeId;
uint136 reserveX;
uint136 reserveY;
uint16 oracleSampleLifetime;
uint16 oracleSize;
uint16 oracleActiveSize;
uint40 oracleLastTimestamp;
uint16 oracleId;
FeesDistribution feesX;
FeesDistribution feesY;
}
/// @dev Structure to store the debts of users/// - debtX: The tokenX's debt/// - debtY: The tokenY's debtstructDebts {
uint256 debtX;
uint256 debtY;
}
/// @dev Structure to store fees:/// - tokenX: The amount of fees of token X/// - tokenY: The amount of fees of token YstructFees {
uint128 tokenX;
uint128 tokenY;
}
/// @dev Structure to minting informations:/// - amountXIn: The amount of token X sent/// - amountYIn: The amount of token Y sent/// - amountXAddedToPair: The amount of token X that have been actually added to the pair/// - amountYAddedToPair: The amount of token Y that have been actually added to the pair/// - activeFeeX: Fees X currently generated/// - activeFeeY: Fees Y currently generated/// - totalDistributionX: Total distribution of token X. Should be 1e18 (100%) or 0 (0%)/// - totalDistributionY: Total distribution of token Y. Should be 1e18 (100%) or 0 (0%)/// - id: Id of the current working bin when looping on the distribution array/// - amountX: The amount of token X deposited in the current bin/// - amountY: The amount of token Y deposited in the current bin/// - distributionX: Distribution of token X for the current working bin/// - distributionY: Distribution of token Y for the current working binstructMintInfo {
uint256 amountXIn;
uint256 amountYIn;
uint256 amountXAddedToPair;
uint256 amountYAddedToPair;
uint256 activeFeeX;
uint256 activeFeeY;
uint256 totalDistributionX;
uint256 totalDistributionY;
uint256 id;
uint256 amountX;
uint256 amountY;
uint256 distributionX;
uint256 distributionY;
}
eventSwap(addressindexed sender,
addressindexed recipient,
uint256indexed id,
bool swapForY,
uint256 amountIn,
uint256 amountOut,
uint256 volatilityAccumulated,
uint256 fees
);
eventFlashLoan(addressindexed sender, addressindexed receiver, IERC20 token, uint256 amount, uint256 fee);
eventCompositionFee(addressindexed sender, addressindexed recipient, uint256indexed id, uint256 feesX, uint256 feesY
);
eventDepositedToBin(addressindexed sender, addressindexed recipient, uint256indexed id, uint256 amountX, uint256 amountY
);
eventWithdrawnFromBin(addressindexed sender, addressindexed recipient, uint256indexed id, uint256 amountX, uint256 amountY
);
eventFeesCollected(addressindexed sender, addressindexed recipient, uint256 amountX, uint256 amountY);
eventProtocolFeesCollected(addressindexed sender, addressindexed recipient, uint256 amountX, uint256 amountY);
eventOracleSizeIncreased(uint256 previousSize, uint256 newSize);
functiontokenX() externalviewreturns (IERC20);
functiontokenY() externalviewreturns (IERC20);
functionfactory() externalviewreturns (address);
functiongetReservesAndId() externalviewreturns (uint256 reserveX, uint256 reserveY, uint256 activeId);
functiongetGlobalFees()
externalviewreturns (uint128 feesXTotal, uint128 feesYTotal, uint128 feesXProtocol, uint128 feesYProtocol);
functiongetOracleParameters()
externalviewreturns (uint256 oracleSampleLifetime,
uint256 oracleSize,
uint256 oracleActiveSize,
uint256 oracleLastTimestamp,
uint256 oracleId,
uint256 min,
uint256 max
);
functiongetOracleSampleFrom(uint256 timeDelta)
externalviewreturns (uint256 cumulativeId, uint256 cumulativeAccumulator, uint256 cumulativeBinCrossed);
functionfeeParameters() externalviewreturns (FeeParameters memory);
functionfindFirstNonEmptyBinId(uint24 id_, bool sentTokenY) externalviewreturns (uint24 id);
functiongetBin(uint24 id) externalviewreturns (uint256 reserveX, uint256 reserveY);
functionpendingFees(address account, uint256[] memory ids)
externalviewreturns (uint256 amountX, uint256 amountY);
functionswap(bool sentTokenY, address to) externalreturns (uint256 amountXOut, uint256 amountYOut);
functionflashLoan(address receiver, IERC20 token, uint256 amount, bytescalldata data) external;
functionmint(uint256[] calldata ids,
uint256[] calldata distributionX,
uint256[] calldata distributionY,
address to
) externalreturns (uint256 amountXAddedToPair, uint256 amountYAddedToPair, uint256[] memory liquidityMinted);
functionburn(uint256[] calldata ids, uint256[] calldata amounts, address to)
externalreturns (uint256 amountX, uint256 amountY);
functionincreaseOracleLength(uint16 newSize) external;
functioncollectFees(address account, uint256[] calldata ids) externalreturns (uint256 amountX, uint256 amountY);
functioncollectProtocolFees() externalreturns (uint128 amountX, uint128 amountY);
functionsetFeesParameters(bytes32 packedFeeParameters) external;
functionforceDecay() external;
functioninitialize(
IERC20 tokenX,
IERC20 tokenY,
uint24 activeId,
uint16 sampleLifetime,
bytes32 packedFeeParameters
) external;
}
// SPDX-License-Identifier: GPL-3.0pragmasolidity ^0.8.10;/**
* @title Liquidity Book Joe Library Helper Library
* @author Trader Joe
* @notice Helper contract used for Joe V1 related calculations
*/libraryJoeLibrary{
errorJoeLibrary__AddressZero();
errorJoeLibrary__IdenticalAddresses();
errorJoeLibrary__InsufficientAmount();
errorJoeLibrary__InsufficientLiquidity();
// returns sorted token addresses, used to handle return values from pairs sorted in this orderfunctionsortTokens(address tokenA, address tokenB) internalpurereturns (address token0, address token1) {
if (tokenA == tokenB) revert JoeLibrary__IdenticalAddresses();
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
if (token0 ==address(0)) revert JoeLibrary__AddressZero();
}
// given some amount of an asset and pair reserves, returns an equivalent amount of the other assetfunctionquote(uint256 amountA, uint256 reserveA, uint256 reserveB) internalpurereturns (uint256 amountB) {
if (amountA ==0) revert JoeLibrary__InsufficientAmount();
if (reserveA ==0|| reserveB ==0) revert JoeLibrary__InsufficientLiquidity();
amountB = (amountA * reserveB) / reserveA;
}
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other assetfunctiongetAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
internalpurereturns (uint256 amountOut)
{
if (amountIn ==0) revert JoeLibrary__InsufficientAmount();
if (reserveIn ==0|| reserveOut ==0) revert JoeLibrary__InsufficientLiquidity();
uint256 amountInWithFee = amountIn *997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn *1000+ amountInWithFee;
amountOut = numerator / denominator;
}
// given an output amount of an asset and pair reserves, returns a required input amount of the other assetfunctiongetAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)
internalpurereturns (uint256 amountIn)
{
if (amountOut ==0) revert JoeLibrary__InsufficientAmount();
if (reserveIn ==0|| reserveOut ==0) revert JoeLibrary__InsufficientLiquidity();
uint256 numerator = reserveIn * amountOut *1000;
uint256 denominator = (reserveOut - amountOut) *997;
amountIn = numerator / denominator +1;
}
}
Contract Source Code
File 24 of 33: LBRouter.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;import {Ownable} from"@openzeppelin/contracts/access/Ownable.sol";
import {BinHelper} from"./libraries/BinHelper.sol";
import {Constants} from"./libraries/Constants.sol";
import {Encoded} from"./libraries/math/Encoded.sol";
import {FeeHelper} from"./libraries/FeeHelper.sol";
import {JoeLibrary} from"./libraries/JoeLibrary.sol";
import {LiquidityConfigurations} from"./libraries/math/LiquidityConfigurations.sol";
import {PackedUint128Math} from"./libraries/math/PackedUint128Math.sol";
import {TokenHelper, IERC20} from"./libraries/TokenHelper.sol";
import {Uint256x256Math} from"./libraries/math/Uint256x256Math.sol";
import {IJoePair} from"./interfaces/IJoePair.sol";
import {ILBPair} from"./interfaces/ILBPair.sol";
import {ILBLegacyPair} from"./interfaces/ILBLegacyPair.sol";
import {ILBToken} from"./interfaces/ILBToken.sol";
import {ILBRouter} from"./interfaces/ILBRouter.sol";
import {ILBLegacyRouter} from"./interfaces/ILBLegacyRouter.sol";
import {IJoeFactory} from"./interfaces/IJoeFactory.sol";
import {ILBLegacyFactory} from"./interfaces/ILBLegacyFactory.sol";
import {ILBFactory} from"./interfaces/ILBFactory.sol";
import {IWNATIVE} from"./interfaces/IWNATIVE.sol";
/**
* @title Liquidity Book Router
* @author Trader Joe
* @notice Main contract to interact with to swap and manage liquidity on Joe V2 exchange.
*/contractLBRouterisILBRouter{
usingTokenHelperforIERC20;
usingJoeLibraryforuint256;
usingPackedUint128Mathforbytes32;
ILBFactory privateimmutable _factory2_2;
ILBFactory privateimmutable _factory2_1;
IJoeFactory privateimmutable _factoryV1;
ILBLegacyFactory privateimmutable _legacyFactory;
ILBLegacyRouter privateimmutable _legacyRouter;
IWNATIVE privateimmutable _wnative;
modifieronlyFactoryOwner() {
if (msg.sender!= Ownable(address(_factory2_2)).owner()) revert LBRouter__NotFactoryOwner();
_;
}
modifierensure(uint256 deadline) {
if (block.timestamp> deadline) revert LBRouter__DeadlineExceeded(deadline, block.timestamp);
_;
}
modifierverifyPathValidity(Path memory path) {
if (
path.pairBinSteps.length==0|| path.versions.length!= path.pairBinSteps.length|| path.pairBinSteps.length+1!= path.tokenPath.length
) revert LBRouter__LengthsMismatch();
_;
}
/**
* @notice Constructor
* @param factory2_2 Address of Joe V2.2 factory
* @param factory2_1 Address of Joe V2.1 factory
* @param factoryV1 Address of Joe V1 factory
* @param legacyFactory Address of Joe V2 factory
* @param legacyRouter Address of Joe V2 router
* @param wnative Address of WNATIVE
*/constructor(
ILBFactory factory2_2,
IJoeFactory factoryV1,
ILBLegacyFactory legacyFactory,
ILBLegacyRouter legacyRouter,
ILBFactory factory2_1,
IWNATIVE wnative
) {
_factory2_2 = factory2_2;
_factory2_1 = factory2_1;
_factoryV1 = factoryV1;
_legacyFactory = legacyFactory;
_legacyRouter = legacyRouter;
_wnative = wnative;
}
/**
* @dev Receive function that only accept NATIVE from the WNATIVE contract
*/receive() externalpayable{
if (msg.sender!=address(_wnative)) revert LBRouter__SenderIsNotWNATIVE();
}
/**
* View function to get the factory V2.1 address
* @return lbFactory The address of the factory V2.1
*/functiongetFactory() externalviewoverridereturns (ILBFactory lbFactory) {
return _factory2_2;
}
/**
* View function to get the factory V2.1 address
* @return lbFactory The address of the factory V2.1
*/functiongetFactoryV2_1() externalviewoverridereturns (ILBFactory lbFactory) {
return _factory2_1;
}
/**
* View function to get the factory V2 address
* @return legacyLBfactory The address of the factory V2
*/functiongetLegacyFactory() externalviewoverridereturns (ILBLegacyFactory legacyLBfactory) {
return _legacyFactory;
}
/**
* View function to get the factory V1 address
* @return factoryV1 The address of the factory V1
*/functiongetV1Factory() externalviewoverridereturns (IJoeFactory factoryV1) {
return _factoryV1;
}
/**
* View function to get the router V2 address
* @return legacyRouter The address of the router V2
*/functiongetLegacyRouter() externalviewoverridereturns (ILBLegacyRouter legacyRouter) {
return _legacyRouter;
}
/**
* View function to get the WNATIVE address
* @return wnative The address of WNATIVE
*/functiongetWNATIVE() externalviewoverridereturns (IWNATIVE wnative) {
return _wnative;
}
/**
* @notice Returns the approximate id corresponding to the inputted price.
* Warning, the returned id may be inaccurate close to the start price of a bin
* @param pair The address of the LBPair
* @param price The price of y per x (multiplied by 1e36)
* @return The id corresponding to this price
*/functiongetIdFromPrice(ILBPair pair, uint256 price) externalviewoverridereturns (uint24) {
return pair.getIdFromPrice(price);
}
/**
* @notice Returns the price corresponding to the inputted id
* @param pair The address of the LBPair
* @param id The id
* @return The price corresponding to this id
*/functiongetPriceFromId(ILBPair pair, uint24 id) externalviewoverridereturns (uint256) {
return pair.getPriceFromId(id);
}
/**
* @notice Simulate a swap in
* @param pair The address of the LBPair
* @param amountOut The amount of token to receive
* @param swapForY Whether you swap X for Y (true), or Y for X (false)
* @return amountIn The amount of token to send in order to receive amountOut token
* @return amountOutLeft The amount of token Out that can't be returned due to a lack of liquidity
* @return fee The amount of fees paid in token sent
*/functiongetSwapIn(ILBPair pair, uint128 amountOut, bool swapForY)
publicviewoverridereturns (uint128 amountIn, uint128 amountOutLeft, uint128 fee)
{
(amountIn, amountOutLeft, fee) = pair.getSwapIn(amountOut, swapForY);
}
/**
* @notice Simulate a swap out
* @param pair The address of the LBPair
* @param amountIn The amount of token sent
* @param swapForY Whether you swap X for Y (true), or Y for X (false)
* @return amountInLeft The amount of token In that can't be swapped due to a lack of liquidity
* @return amountOut The amount of token received if amountIn tokenX are sent
* @return fee The amount of fees paid in token sent
*/functiongetSwapOut(ILBPair pair, uint128 amountIn, bool swapForY)
externalviewoverridereturns (uint128 amountInLeft, uint128 amountOut, uint128 fee)
{
(amountInLeft, amountOut, fee) = pair.getSwapOut(amountIn, swapForY);
}
/**
* @notice Create a liquidity bin LBPair for tokenX and tokenY using the factory
* @param tokenX The address of the first token
* @param tokenY The address of the second token
* @param activeId The active id of the pair
* @param binStep The bin step in basis point, used to calculate log(1 + binStep)
* @return pair The address of the newly created LBPair
*/functioncreateLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
externaloverridereturns (ILBPair pair)
{
pair = _factory2_2.createLBPair(tokenX, tokenY, activeId, binStep);
}
/**
* @notice Add liquidity while performing safety checks
* @dev This function is compliant with fee on transfer tokens
* @param liquidityParameters The liquidity parameters
* @return amountXAdded The amount of token X added
* @return amountYAdded The amount of token Y added
* @return amountXLeft The amount of token X left (sent back to liquidityParameters.refundTo)
* @return amountYLeft The amount of token Y left (sent back to liquidityParameters.refundTo)
* @return depositIds The ids of the deposits
* @return liquidityMinted The amount of liquidity minted
*/functionaddLiquidity(LiquidityParameters calldata liquidityParameters)
externaloverridereturns (uint256 amountXAdded,
uint256 amountYAdded,
uint256 amountXLeft,
uint256 amountYLeft,
uint256[] memory depositIds,
uint256[] memory liquidityMinted
)
{
ILBPair lbPair = ILBPair(
_getLBPairInformation(
liquidityParameters.tokenX, liquidityParameters.tokenY, liquidityParameters.binStep, Version.V2_2
)
);
if (liquidityParameters.tokenX != lbPair.getTokenX()) revert LBRouter__WrongTokenOrder();
_safeTransferFrom(liquidityParameters.tokenX, msg.sender, address(lbPair), liquidityParameters.amountX);
_safeTransferFrom(liquidityParameters.tokenY, msg.sender, address(lbPair), liquidityParameters.amountY);
(amountXAdded, amountYAdded, amountXLeft, amountYLeft, depositIds, liquidityMinted) =
_addLiquidity(liquidityParameters, lbPair);
}
/**
* @notice Add liquidity with NATIVE while performing safety checks
* @dev This function is compliant with fee on transfer tokens
* @param liquidityParameters The liquidity parameters
* @return amountXAdded The amount of token X added
* @return amountYAdded The amount of token Y added
* @return amountXLeft The amount of token X left (sent back to liquidityParameters.refundTo)
* @return amountYLeft The amount of token Y left (sent back to liquidityParameters.refundTo)
* @return depositIds The ids of the deposits
* @return liquidityMinted The amount of liquidity minted
*/functionaddLiquidityNATIVE(LiquidityParameters calldata liquidityParameters)
externalpayableoverridereturns (uint256 amountXAdded,
uint256 amountYAdded,
uint256 amountXLeft,
uint256 amountYLeft,
uint256[] memory depositIds,
uint256[] memory liquidityMinted
)
{
ILBPair _LBPair = ILBPair(
_getLBPairInformation(
liquidityParameters.tokenX, liquidityParameters.tokenY, liquidityParameters.binStep, Version.V2_2
)
);
if (liquidityParameters.tokenX != _LBPair.getTokenX()) revert LBRouter__WrongTokenOrder();
if (liquidityParameters.tokenX == _wnative && liquidityParameters.amountX ==msg.value) {
_safeTransferFrom(liquidityParameters.tokenY, msg.sender, address(_LBPair), liquidityParameters.amountY);
_wNativeDepositAndTransfer(address(_LBPair), msg.value);
} elseif (liquidityParameters.tokenY == _wnative && liquidityParameters.amountY ==msg.value) {
_safeTransferFrom(liquidityParameters.tokenX, msg.sender, address(_LBPair), liquidityParameters.amountX);
_wNativeDepositAndTransfer(address(_LBPair), msg.value);
} else {
revert LBRouter__WrongNativeLiquidityParameters(
address(liquidityParameters.tokenX),
address(liquidityParameters.tokenY),
liquidityParameters.amountX,
liquidityParameters.amountY,
msg.value
);
}
(amountXAdded, amountYAdded, amountXLeft, amountYLeft, depositIds, liquidityMinted) =
_addLiquidity(liquidityParameters, _LBPair);
}
/**
* @notice Remove liquidity while performing safety checks
* @dev This function is compliant with fee on transfer tokens
* @param tokenX The address of token X
* @param tokenY The address of token Y
* @param binStep The bin step of the LBPair
* @param amountXMin The min amount to receive of token X
* @param amountYMin The min amount to receive of token Y
* @param ids The list of ids to burn
* @param amounts The list of amounts to burn of each id in `_ids`
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountX Amount of token X returned
* @return amountY Amount of token Y returned
*/functionremoveLiquidity(
IERC20 tokenX,
IERC20 tokenY,
uint16 binStep,
uint256 amountXMin,
uint256 amountYMin,
uint256[] memory ids,
uint256[] memory amounts,
address to,
uint256 deadline
) externaloverrideensure(deadline) returns (uint256 amountX, uint256 amountY) {
ILBPair _LBPair = ILBPair(_getLBPairInformation(tokenX, tokenY, binStep, Version.V2_2));
bool isWrongOrder = tokenX != _LBPair.getTokenX();
if (isWrongOrder) (amountXMin, amountYMin) = (amountYMin, amountXMin);
(amountX, amountY) = _removeLiquidity(_LBPair, amountXMin, amountYMin, ids, amounts, to);
if (isWrongOrder) (amountX, amountY) = (amountY, amountX);
}
/**
* @notice Remove NATIVE liquidity while performing safety checks
* @dev This function is **NOT** compliant with fee on transfer tokens.
* This is wanted as it would make users pays the fee on transfer twice,
* use the `removeLiquidity` function to remove liquidity with fee on transfer tokens.
* @param token The address of token
* @param binStep The bin step of the LBPair
* @param amountTokenMin The min amount to receive of token
* @param amountNATIVEMin The min amount to receive of NATIVE
* @param ids The list of ids to burn
* @param amounts The list of amounts to burn of each id in `_ids`
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountToken Amount of token returned
* @return amountNATIVE Amount of NATIVE returned
*/functionremoveLiquidityNATIVE(
IERC20 token,
uint16 binStep,
uint256 amountTokenMin,
uint256 amountNATIVEMin,
uint256[] memory ids,
uint256[] memory amounts,
addresspayable to,
uint256 deadline
) externaloverrideensure(deadline) returns (uint256 amountToken, uint256 amountNATIVE) {
ILBPair lbPair = ILBPair(_getLBPairInformation(token, IERC20(_wnative), binStep, Version.V2_2));
{
bool isNATIVETokenY = IERC20(_wnative) == lbPair.getTokenY();
if (!isNATIVETokenY) {
(amountTokenMin, amountNATIVEMin) = (amountNATIVEMin, amountTokenMin);
}
(uint256 amountX, uint256 amountY) =
_removeLiquidity(lbPair, amountTokenMin, amountNATIVEMin, ids, amounts, address(this));
(amountToken, amountNATIVE) = isNATIVETokenY ? (amountX, amountY) : (amountY, amountX);
}
_safeTransfer(token, to, amountToken);
_wNativeWithdrawAndTransfer(to, amountNATIVE);
}
/**
* @notice Swaps exact tokens for tokens while performing safety checks
* @param amountIn The amount of token to send
* @param amountOutMin The min amount of token to receive
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountOut Output amount of the swap
*/functionswapExactTokensForTokens(uint256 amountIn,
uint256 amountOutMin,
Path memory path,
address to,
uint256 deadline
) externaloverrideensure(deadline) verifyPathValidity(path) returns (uint256 amountOut) {
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
_safeTransferFrom(path.tokenPath[0], msg.sender, pairs[0], amountIn);
amountOut = _swapExactTokensForTokens(amountIn, pairs, path.versions, path.tokenPath, to);
if (amountOutMin > amountOut) revert LBRouter__InsufficientAmountOut(amountOutMin, amountOut);
}
/**
* @notice Swaps exact tokens for NATIVE while performing safety checks
* @param amountIn The amount of token to send
* @param amountOutMinNATIVE The min amount of NATIVE to receive
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountOut Output amount of the swap
*/functionswapExactTokensForNATIVE(uint256 amountIn,
uint256 amountOutMinNATIVE,
Path memory path,
addresspayable to,
uint256 deadline
) externaloverrideensure(deadline) verifyPathValidity(path) returns (uint256 amountOut) {
if (path.tokenPath[path.pairBinSteps.length] != IERC20(_wnative)) {
revert LBRouter__InvalidTokenPath(address(path.tokenPath[path.pairBinSteps.length]));
}
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
_safeTransferFrom(path.tokenPath[0], msg.sender, pairs[0], amountIn);
amountOut = _swapExactTokensForTokens(amountIn, pairs, path.versions, path.tokenPath, address(this));
if (amountOutMinNATIVE > amountOut) revert LBRouter__InsufficientAmountOut(amountOutMinNATIVE, amountOut);
_wNativeWithdrawAndTransfer(to, amountOut);
}
/**
* @notice Swaps exact NATIVE for tokens while performing safety checks
* @param amountOutMin The min amount of token to receive
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountOut Output amount of the swap
*/functionswapExactNATIVEForTokens(uint256 amountOutMin, Path memory path, address to, uint256 deadline)
externalpayableoverrideensure(deadline)
verifyPathValidity(path)
returns (uint256 amountOut)
{
if (path.tokenPath[0] != IERC20(_wnative)) revert LBRouter__InvalidTokenPath(address(path.tokenPath[0]));
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
_wNativeDepositAndTransfer(pairs[0], msg.value);
amountOut = _swapExactTokensForTokens(msg.value, pairs, path.versions, path.tokenPath, to);
if (amountOutMin > amountOut) revert LBRouter__InsufficientAmountOut(amountOutMin, amountOut);
}
/**
* @notice Swaps tokens for exact tokens while performing safety checks
* @param amountOut The amount of token to receive
* @param amountInMax The max amount of token to send
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountsIn Input amounts of the swap
*/functionswapTokensForExactTokens(uint256 amountOut,
uint256 amountInMax,
Path memory path,
address to,
uint256 deadline
) externaloverrideensure(deadline) verifyPathValidity(path) returns (uint256[] memory amountsIn) {
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
{
amountsIn = _getAmountsIn(path.versions, pairs, path.tokenPath, amountOut);
if (amountsIn[0] > amountInMax) revert LBRouter__MaxAmountInExceeded(amountInMax, amountsIn[0]);
_safeTransferFrom(path.tokenPath[0], msg.sender, pairs[0], amountsIn[0]);
uint256 _amountOutReal = _swapTokensForExactTokens(pairs, path.versions, path.tokenPath, amountsIn, to);
if (_amountOutReal < amountOut) revert LBRouter__InsufficientAmountOut(amountOut, _amountOutReal);
}
}
/**
* @notice Swaps tokens for exact NATIVE while performing safety checks
* @param amountNATIVEOut The amount of NATIVE to receive
* @param amountInMax The max amount of token to send
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountsIn path amounts for every step of the swap
*/functionswapTokensForExactNATIVE(uint256 amountNATIVEOut,
uint256 amountInMax,
Path memory path,
addresspayable to,
uint256 deadline
) externaloverrideensure(deadline) verifyPathValidity(path) returns (uint256[] memory amountsIn) {
if (path.tokenPath[path.pairBinSteps.length] != IERC20(_wnative)) {
revert LBRouter__InvalidTokenPath(address(path.tokenPath[path.pairBinSteps.length]));
}
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
amountsIn = _getAmountsIn(path.versions, pairs, path.tokenPath, amountNATIVEOut);
if (amountsIn[0] > amountInMax) revert LBRouter__MaxAmountInExceeded(amountInMax, amountsIn[0]);
_safeTransferFrom(path.tokenPath[0], msg.sender, pairs[0], amountsIn[0]);
uint256 _amountOutReal =
_swapTokensForExactTokens(pairs, path.versions, path.tokenPath, amountsIn, address(this));
if (_amountOutReal < amountNATIVEOut) revert LBRouter__InsufficientAmountOut(amountNATIVEOut, _amountOutReal);
_wNativeWithdrawAndTransfer(to, _amountOutReal);
}
/**
* @notice Swaps NATIVE for exact tokens while performing safety checks
* @dev Will refund any NATIVE amount sent in excess to `msg.sender`
* @param amountOut The amount of tokens to receive
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountsIn path amounts for every step of the swap
*/functionswapNATIVEForExactTokens(uint256 amountOut, Path memory path, address to, uint256 deadline)
externalpayableoverrideensure(deadline)
verifyPathValidity(path)
returns (uint256[] memory amountsIn)
{
if (path.tokenPath[0] != IERC20(_wnative)) revert LBRouter__InvalidTokenPath(address(path.tokenPath[0]));
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
amountsIn = _getAmountsIn(path.versions, pairs, path.tokenPath, amountOut);
if (amountsIn[0] >msg.value) revert LBRouter__MaxAmountInExceeded(msg.value, amountsIn[0]);
_wNativeDepositAndTransfer(pairs[0], amountsIn[0]);
uint256 amountOutReal = _swapTokensForExactTokens(pairs, path.versions, path.tokenPath, amountsIn, to);
if (amountOutReal < amountOut) revert LBRouter__InsufficientAmountOut(amountOut, amountOutReal);
if (msg.value> amountsIn[0]) _safeTransferNative(msg.sender, msg.value- amountsIn[0]);
}
/**
* @notice Swaps exact tokens for tokens while performing safety checks supporting for fee on transfer tokens
* @param amountIn The amount of token to send
* @param amountOutMin The min amount of token to receive
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountOut Output amount of the swap
*/functionswapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn,
uint256 amountOutMin,
Path memory path,
address to,
uint256 deadline
) externaloverrideensure(deadline) verifyPathValidity(path) returns (uint256 amountOut) {
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
IERC20 targetToken = path.tokenPath[pairs.length];
uint256 balanceBefore = targetToken.balanceOf(to);
_safeTransferFrom(path.tokenPath[0], msg.sender, pairs[0], amountIn);
_swapSupportingFeeOnTransferTokens(pairs, path.versions, path.tokenPath, to);
amountOut = targetToken.balanceOf(to) - balanceBefore;
if (amountOutMin > amountOut) revert LBRouter__InsufficientAmountOut(amountOutMin, amountOut);
}
/**
* @notice Swaps exact tokens for NATIVE while performing safety checks supporting for fee on transfer tokens
* @param amountIn The amount of token to send
* @param amountOutMinNATIVE The min amount of NATIVE to receive
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountOut Output amount of the swap
*/functionswapExactTokensForNATIVESupportingFeeOnTransferTokens(uint256 amountIn,
uint256 amountOutMinNATIVE,
Path memory path,
addresspayable to,
uint256 deadline
) externaloverrideensure(deadline) verifyPathValidity(path) returns (uint256 amountOut) {
if (path.tokenPath[path.pairBinSteps.length] != IERC20(_wnative)) {
revert LBRouter__InvalidTokenPath(address(path.tokenPath[path.pairBinSteps.length]));
}
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
uint256 balanceBefore = _wnative.balanceOf(address(this));
_safeTransferFrom(path.tokenPath[0], msg.sender, pairs[0], amountIn);
_swapSupportingFeeOnTransferTokens(pairs, path.versions, path.tokenPath, address(this));
amountOut = _wnative.balanceOf(address(this)) - balanceBefore;
if (amountOutMinNATIVE > amountOut) revert LBRouter__InsufficientAmountOut(amountOutMinNATIVE, amountOut);
_wNativeWithdrawAndTransfer(to, amountOut);
}
/**
* @notice Swaps exact NATIVE for tokens while performing safety checks supporting for fee on transfer tokens
* @param amountOutMin The min amount of token to receive
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountOut Output amount of the swap
*/functionswapExactNATIVEForTokensSupportingFeeOnTransferTokens(uint256 amountOutMin,
Path memory path,
address to,
uint256 deadline
) externalpayableoverrideensure(deadline) verifyPathValidity(path) returns (uint256 amountOut) {
if (path.tokenPath[0] != IERC20(_wnative)) revert LBRouter__InvalidTokenPath(address(path.tokenPath[0]));
address[] memory pairs = _getPairs(path.pairBinSteps, path.versions, path.tokenPath);
IERC20 targetToken = path.tokenPath[pairs.length];
uint256 balanceBefore = targetToken.balanceOf(to);
_wNativeDepositAndTransfer(pairs[0], msg.value);
_swapSupportingFeeOnTransferTokens(pairs, path.versions, path.tokenPath, to);
amountOut = targetToken.balanceOf(to) - balanceBefore;
if (amountOutMin > amountOut) revert LBRouter__InsufficientAmountOut(amountOutMin, amountOut);
}
/**
* @notice Unstuck tokens that are sent to this contract by mistake
* @dev Only callable by the factory owner
* @param token The address of the token
* @param to The address of the user to send back the tokens
* @param amount The amount to send
*/functionsweep(IERC20 token, address to, uint256 amount) externaloverrideonlyFactoryOwner{
if (address(token) ==address(0)) {
amount = amount ==type(uint256).max ? address(this).balance : amount;
_safeTransferNative(to, amount);
} else {
amount = amount ==type(uint256).max ? token.balanceOf(address(this)) : amount;
token.safeTransfer(to, amount);
}
}
/**
* @notice Unstuck LBTokens that are sent to this contract by mistake
* @dev Only callable by the factory owner
* @param lbToken The address of the LBToken
* @param to The address of the user to send back the tokens
* @param ids The list of token ids
* @param amounts The list of amounts to send
*/functionsweepLBToken(ILBToken lbToken, address to, uint256[] calldata ids, uint256[] calldata amounts)
externaloverrideonlyFactoryOwner{
lbToken.batchTransferFrom(address(this), to, ids, amounts);
}
/**
* @notice Helper function to add liquidity
* @param liq The liquidity parameter
* @param pair LBPair where liquidity is deposited
* @return amountXAdded Amount of token X added
* @return amountYAdded Amount of token Y added
* @return amountXLeft Amount of token X left
* @return amountYLeft Amount of token Y left
* @return depositIds The list of deposit ids
* @return liquidityMinted The list of liquidity minted
*/function_addLiquidity(LiquidityParameters calldata liq, ILBPair pair)
privateensure(liq.deadline)
returns (uint256 amountXAdded,
uint256 amountYAdded,
uint256 amountXLeft,
uint256 amountYLeft,
uint256[] memory depositIds,
uint256[] memory liquidityMinted
)
{
unchecked {
if (liq.deltaIds.length!= liq.distributionX.length|| liq.deltaIds.length!= liq.distributionY.length) {
revert LBRouter__LengthsMismatch();
}
if (liq.activeIdDesired >type(uint24).max|| liq.idSlippage >type(uint24).max) {
revert LBRouter__IdDesiredOverflows(liq.activeIdDesired, liq.idSlippage);
}
bytes32[] memory liquidityConfigs =newbytes32[](liq.deltaIds.length);
depositIds =newuint256[](liq.deltaIds.length);
{
uint256 _activeId = pair.getActiveId();
if (
liq.activeIdDesired + liq.idSlippage < _activeId || _activeId + liq.idSlippage < liq.activeIdDesired
) {
revert LBRouter__IdSlippageCaught(liq.activeIdDesired, liq.idSlippage, _activeId);
}
for (uint256 i; i < liquidityConfigs.length; ++i) {
int256 _id =int256(_activeId) + liq.deltaIds[i];
if (_id <0||uint256(_id) >type(uint24).max) revert LBRouter__IdOverflows(_id);
depositIds[i] =uint256(_id);
liquidityConfigs[i] = LiquidityConfigurations.encodeParams(
uint64(liq.distributionX[i]), uint64(liq.distributionY[i]), uint24(uint256(_id))
);
}
}
bytes32 amountsReceived;
bytes32 amountsLeft;
(amountsReceived, amountsLeft, liquidityMinted) = pair.mint(liq.to, liquidityConfigs, liq.refundTo);
bytes32 amountsAdded = amountsReceived.sub(amountsLeft);
amountXAdded = amountsAdded.decodeX();
amountYAdded = amountsAdded.decodeY();
if (amountXAdded < liq.amountXMin || amountYAdded < liq.amountYMin) {
revert LBRouter__AmountSlippageCaught(liq.amountXMin, amountXAdded, liq.amountYMin, amountYAdded);
}
amountXLeft = amountsLeft.decodeX();
amountYLeft = amountsLeft.decodeY();
for (uint256 i =0; i < depositIds.length; i++) {
if (liquidityMinted[i] < Constants.MIN_LIQUIDITY_PER_BIN) {
revert LBRouter__InsufficientLiquidityMinted(liquidityMinted[i]);
}
}
}
}
/**
* @notice Helper function to return the amounts in
* @param versions The list of versions (V1, V2, V2_1 or V2_2)
* @param pairs The list of pairs
* @param tokenPath The swap path
* @param amountOut The amount out
* @return amountsIn The list of amounts in
*/function_getAmountsIn(
Version[] memory versions,
address[] memory pairs,
IERC20[] memory tokenPath,
uint256 amountOut
) privateviewreturns (uint256[] memory amountsIn) {
amountsIn =newuint256[](tokenPath.length);
// Avoid doing -1, as `pairs.length == pairBinSteps.length-1`
amountsIn[pairs.length] = amountOut;
for (uint256 i = pairs.length; i !=0; i--) {
IERC20 token = tokenPath[i -1];
Version version = versions[i -1];
address pair = pairs[i -1];
if (version == Version.V1) {
(uint256 reserveIn, uint256 reserveOut,) = IJoePair(pair).getReserves();
if (token > tokenPath[i]) {
(reserveIn, reserveOut) = (reserveOut, reserveIn);
}
uint256 amountOut_ = amountsIn[i];
amountsIn[i -1] =uint128(amountOut_.getAmountIn(reserveIn, reserveOut));
} elseif (version == Version.V2) {
(amountsIn[i -1],) = _legacyRouter.getSwapIn(
ILBLegacyPair(pair), uint128(amountsIn[i]), ILBLegacyPair(pair).tokenX() == token
);
} else {
(amountsIn[i -1],,) =
getSwapIn(ILBPair(pair), uint128(amountsIn[i]), ILBPair(pair).getTokenX() == token);
}
}
}
/**
* @notice Helper function to remove liquidity
* @param pair The address of the LBPair
* @param amountXMin The min amount to receive of token X
* @param amountYMin The min amount to receive of token Y
* @param ids The list of ids to burn
* @param amounts The list of amounts to burn of each id in `_ids`
* @param to The address of the recipient
* @return amountX The amount of token X sent by the pair
* @return amountY The amount of token Y sent by the pair
*/function_removeLiquidity(
ILBPair pair,
uint256 amountXMin,
uint256 amountYMin,
uint256[] memory ids,
uint256[] memory amounts,
address to
) privatereturns (uint256 amountX, uint256 amountY) {
(bytes32[] memory amountsBurned) = pair.burn(msg.sender, to, ids, amounts);
for (uint256 i; i < amountsBurned.length; ++i) {
amountX += amountsBurned[i].decodeX();
amountY += amountsBurned[i].decodeY();
}
if (amountX < amountXMin || amountY < amountYMin) {
revert LBRouter__AmountSlippageCaught(amountXMin, amountX, amountYMin, amountY);
}
}
/**
* @notice Helper function to swap exact tokens for tokens
* @param amountIn The amount of token sent
* @param pairs The list of pairs
* @param versions The list of versions (V1, V2, V2_1 or V2_2)
* @param tokenPath The swap path using the binSteps following `pairBinSteps`
* @param to The address of the recipient
* @return amountOut The amount of token sent to `to`
*/function_swapExactTokensForTokens(uint256 amountIn,
address[] memory pairs,
Version[] memory versions,
IERC20[] memory tokenPath,
address to
) privatereturns (uint256 amountOut) {
IERC20 token;
Version version;
address recipient;
address pair;
IERC20 tokenNext = tokenPath[0];
amountOut = amountIn;
unchecked {
for (uint256 i; i < pairs.length; ++i) {
pair = pairs[i];
version = versions[i];
token = tokenNext;
tokenNext = tokenPath[i +1];
recipient = i +1== pairs.length ? to : pairs[i +1];
if (version == Version.V1) {
(uint256 reserve0, uint256 reserve1,) = IJoePair(pair).getReserves();
if (token < tokenNext) {
amountOut = amountOut.getAmountOut(reserve0, reserve1);
IJoePair(pair).swap(0, amountOut, recipient, "");
} else {
amountOut = amountOut.getAmountOut(reserve1, reserve0);
IJoePair(pair).swap(amountOut, 0, recipient, "");
}
} elseif (version == Version.V2) {
bool swapForY = tokenNext == ILBLegacyPair(pair).tokenY();
(uint256 amountXOut, uint256 amountYOut) = ILBLegacyPair(pair).swap(swapForY, recipient);
if (swapForY) amountOut = amountYOut;
else amountOut = amountXOut;
} else {
bool swapForY = tokenNext == ILBPair(pair).getTokenY();
(uint256 amountXOut, uint256 amountYOut) = ILBPair(pair).swap(swapForY, recipient).decode();
if (swapForY) amountOut = amountYOut;
else amountOut = amountXOut;
}
}
}
}
/**
* @notice Helper function to swap tokens for exact tokens
* @param pairs The array of pairs
* @param versions The list of versions (V1, V2, V2_1 or V2_2)
* @param tokenPath The swap path using the binSteps following `pairBinSteps`
* @param amountsIn The list of amounts in
* @param to The address of the recipient
* @return amountOut The amount of token sent to `to`
*/function_swapTokensForExactTokens(address[] memory pairs,
Version[] memory versions,
IERC20[] memory tokenPath,
uint256[] memory amountsIn,
address to
) privatereturns (uint256 amountOut) {
IERC20 token;
address recipient;
address pair;
Version version;
IERC20 tokenNext = tokenPath[0];
unchecked {
for (uint256 i; i < pairs.length; ++i) {
pair = pairs[i];
version = versions[i];
token = tokenNext;
tokenNext = tokenPath[i +1];
recipient = i +1== pairs.length ? to : pairs[i +1];
if (version == Version.V1) {
amountOut = amountsIn[i +1];
if (token < tokenNext) {
IJoePair(pair).swap(0, amountOut, recipient, "");
} else {
IJoePair(pair).swap(amountOut, 0, recipient, "");
}
} elseif (version == Version.V2) {
bool swapForY = tokenNext == ILBLegacyPair(pair).tokenY();
(uint256 amountXOut, uint256 amountYOut) = ILBLegacyPair(pair).swap(swapForY, recipient);
if (swapForY) amountOut = amountYOut;
else amountOut = amountXOut;
} else {
bool swapForY = tokenNext == ILBPair(pair).getTokenY();
(uint256 amountXOut, uint256 amountYOut) = ILBPair(pair).swap(swapForY, recipient).decode();
if (swapForY) amountOut = amountYOut;
else amountOut = amountXOut;
}
}
}
}
/**
* @notice Helper function to swap exact tokens supporting for fee on transfer tokens
* @param pairs The list of pairs
* @param versions The list of versions (V1, V2, V2_1 or V2_2)
* @param tokenPath The swap path using the binSteps following `pairBinSteps`
* @param to The address of the recipient
*/function_swapSupportingFeeOnTransferTokens(address[] memory pairs,
Version[] memory versions,
IERC20[] memory tokenPath,
address to
) private{
IERC20 token;
Version version;
address recipient;
address pair;
IERC20 tokenNext = tokenPath[0];
unchecked {
for (uint256 i; i < pairs.length; ++i) {
pair = pairs[i];
version = versions[i];
token = tokenNext;
tokenNext = tokenPath[i +1];
recipient = i +1== pairs.length ? to : pairs[i +1];
if (version == Version.V1) {
(uint256 _reserve0, uint256 _reserve1,) = IJoePair(pair).getReserves();
if (token < tokenNext) {
uint256 amountIn = token.balanceOf(pair) - _reserve0;
uint256 amountOut = amountIn.getAmountOut(_reserve0, _reserve1);
IJoePair(pair).swap(0, amountOut, recipient, "");
} else {
uint256 amountIn = token.balanceOf(pair) - _reserve1;
uint256 amountOut = amountIn.getAmountOut(_reserve1, _reserve0);
IJoePair(pair).swap(amountOut, 0, recipient, "");
}
} elseif (version == Version.V2) {
ILBLegacyPair(pair).swap(tokenNext == ILBLegacyPair(pair).tokenY(), recipient);
} else {
ILBPair(pair).swap(tokenNext == ILBPair(pair).getTokenY(), recipient);
}
}
}
}
/**
* @notice Helper function to return the address of the LBPair
* @dev Revert if the pair is not created yet
* @param tokenX The address of the tokenX
* @param tokenY The address of the tokenY
* @param binStep The bin step of the LBPair
* @param version The version of the LBPair
* @return lbPair The address of the LBPair
*/function_getLBPairInformation(IERC20 tokenX, IERC20 tokenY, uint256 binStep, Version version)
privateviewreturns (address lbPair)
{
if (version == Version.V2) {
lbPair =address(_legacyFactory.getLBPairInformation(tokenX, tokenY, binStep).LBPair);
} elseif (version == Version.V2_1) {
lbPair =address(_factory2_1.getLBPairInformation(tokenX, tokenY, binStep).LBPair);
} else {
lbPair =address(_factory2_2.getLBPairInformation(tokenX, tokenY, binStep).LBPair);
}
if (lbPair ==address(0)) {
revert LBRouter__PairNotCreated(address(tokenX), address(tokenY), binStep);
}
}
/**
* @notice Helper function to return the address of the pair (v1 or v2, according to `binStep`)
* @dev Revert if the pair is not created yet
* @param tokenX The address of the tokenX
* @param tokenY The address of the tokenY
* @param binStep The bin step of the LBPair
* @param version The version of the LBPair
* @return pair The address of the pair of binStep `binStep`
*/function_getPair(IERC20 tokenX, IERC20 tokenY, uint256 binStep, Version version)
privateviewreturns (address pair)
{
if (version == Version.V1) {
pair = _factoryV1.getPair(address(tokenX), address(tokenY));
if (pair ==address(0)) revert LBRouter__PairNotCreated(address(tokenX), address(tokenY), binStep);
} else {
pair =address(_getLBPairInformation(tokenX, tokenY, binStep, version));
}
}
/**
* @notice Helper function to return a list of pairs
* @param pairBinSteps The list of bin steps
* @param versions The list of versions (V1, V2, V2_1 or V2_2)
* @param tokenPath The swap path using the binSteps following `pairBinSteps`
* @return pairs The list of pairs
*/function_getPairs(uint256[] memory pairBinSteps, Version[] memory versions, IERC20[] memory tokenPath)
privateviewreturns (address[] memory pairs)
{
pairs =newaddress[](pairBinSteps.length);
IERC20 token;
IERC20 tokenNext = tokenPath[0];
unchecked {
for (uint256 i; i < pairs.length; ++i) {
token = tokenNext;
tokenNext = tokenPath[i +1];
pairs[i] = _getPair(token, tokenNext, pairBinSteps[i], versions[i]);
}
}
}
/**
* @notice Helper function to transfer tokens to `to`
* @param token The address of the token
* @param to The address of the recipient
* @param amount The amount to send
*/function_safeTransfer(IERC20 token, address to, uint256 amount) private{
if (amount ==0) return;
token.safeTransfer(to, amount);
}
/**
* @notice Helper function to transfer tokens from `from` to `to`
* @param token The address of the token
* @param from The address of the sender
* @param to The address of the recipient
* @param amount The amount to send
*/function_safeTransferFrom(IERC20 token, addressfrom, address to, uint256 amount) private{
if (amount ==0) return;
token.safeTransferFrom(from, to, amount);
}
/**
* @notice Helper function to transfer NATIVE to `to`
* @param to The address of the recipient
* @param amount The amount to send
*/function_safeTransferNative(address to, uint256 amount) private{
if (amount ==0) return;
(bool success,) = to.call{value: amount}("");
if (!success) revert LBRouter__FailedToSendNATIVE(to, amount);
}
/**
* @notice Helper function to deposit and transfer WNative to `to`
* @param to The address of the recipient
* @param amount The amount to deposit and transfer
*/function_wNativeDepositAndTransfer(address to, uint256 amount) private{
if (amount ==0) return;
_wnative.deposit{value: amount}();
IERC20(_wnative).safeTransfer(to, amount);
}
/**
* @notice Helper function to withdraw and transfer WNative to `to`
* @param to The address of the recipient
* @param amount The amount to withdraw and transfer
*/function_wNativeWithdrawAndTransfer(address to, uint256 amount) private{
if (amount ==0) return;
_wnative.withdraw(amount);
_safeTransferNative(to, amount);
}
}
Contract Source Code
File 25 of 33: LiquidityConfigurations.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {PackedUint128Math} from"./PackedUint128Math.sol";
import {Encoded} from"./Encoded.sol";
/**
* @title Liquidity Book Liquidity Configurations Library
* @author Trader Joe
* @notice This library contains functions to encode and decode the config of a pool and interact with the encoded bytes32.
*/libraryLiquidityConfigurations{
usingPackedUint128Mathforbytes32;
usingPackedUint128Mathforuint128;
usingEncodedforbytes32;
errorLiquidityConfigurations__InvalidConfig();
uint256privateconstant OFFSET_ID =0;
uint256privateconstant OFFSET_DISTRIBUTION_Y =24;
uint256privateconstant OFFSET_DISTRIBUTION_X =88;
uint256privateconstant PRECISION =1e18;
/**
* @dev Encode the distributionX, distributionY and id into a single bytes32
* @param distributionX The distribution of the first token
* @param distributionY The distribution of the second token
* @param id The id of the pool
* @return config The encoded config as follows:
* [0 - 24[: id
* [24 - 88[: distributionY
* [88 - 152[: distributionX
* [152 - 256[: empty
*/functionencodeParams(uint64 distributionX, uint64 distributionY, uint24 id)
internalpurereturns (bytes32 config)
{
config = config.set(distributionX, Encoded.MASK_UINT64, OFFSET_DISTRIBUTION_X);
config = config.set(distributionY, Encoded.MASK_UINT64, OFFSET_DISTRIBUTION_Y);
config = config.set(id, Encoded.MASK_UINT24, OFFSET_ID);
}
/**
* @dev Decode the distributionX, distributionY and id from a single bytes32
* @param config The encoded config as follows:
* [0 - 24[: id
* [24 - 88[: distributionY
* [88 - 152[: distributionX
* [152 - 256[: empty
* @return distributionX The distribution of the first token
* @return distributionY The distribution of the second token
* @return id The id of the bin to add the liquidity to
*/functiondecodeParams(bytes32 config)
internalpurereturns (uint64 distributionX, uint64 distributionY, uint24 id)
{
distributionX = config.decodeUint64(OFFSET_DISTRIBUTION_X);
distributionY = config.decodeUint64(OFFSET_DISTRIBUTION_Y);
id = config.decodeUint24(OFFSET_ID);
if (uint256(config) >type(uint152).max|| distributionX > PRECISION || distributionY > PRECISION) {
revert LiquidityConfigurations__InvalidConfig();
}
}
/**
* @dev Get the amounts and id from a config and amountsIn
* @param config The encoded config as follows:
* [0 - 24[: id
* [24 - 88[: distributionY
* [88 - 152[: distributionX
* [152 - 256[: empty
* @param amountsIn The amounts to distribute as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @return amounts The distributed amounts as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @return id The id of the bin to add the liquidity to
*/functiongetAmountsAndId(bytes32 config, bytes32 amountsIn) internalpurereturns (bytes32, uint24) {
(uint64 distributionX, uint64 distributionY, uint24 id) = decodeParams(config);
(uint128 x1, uint128 x2) = amountsIn.decode();
assembly {
x1 :=div(mul(x1, distributionX), PRECISION)
x2 :=div(mul(x2, distributionY), PRECISION)
}
return (x1.encode(x2), id);
}
}
Contract Source Code
File 26 of 33: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)pragmasolidity ^0.8.20;import {Context} from"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/errorOwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/errorOwnableInvalidOwner(address owner);
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/constructor(address initialOwner) {
if (initialOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
if (newOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 27 of 33: PackedUint128Math.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {Constants} from"../Constants.sol";
/**
* @title Liquidity Book Packed Uint128 Math Library
* @author Trader Joe
* @notice This library contains functions to encode and decode two uint128 into a single bytes32
* and interact with the encoded bytes32.
*/libraryPackedUint128Math{
errorPackedUint128Math__AddOverflow();
errorPackedUint128Math__SubUnderflow();
errorPackedUint128Math__MultiplierTooLarge();
uint256privateconstant OFFSET =128;
uint256privateconstant MASK_128 =0xffffffffffffffffffffffffffffffff;
uint256privateconstant MASK_128_PLUS_ONE = MASK_128 +1;
/**
* @dev Encodes two uint128 into a single bytes32
* @param x1 The first uint128
* @param x2 The second uint128
* @return z The encoded bytes32 as follows:
* [0 - 128[: x1
* [128 - 256[: x2
*/functionencode(uint128 x1, uint128 x2) internalpurereturns (bytes32 z) {
assembly {
z :=or(and(x1, MASK_128), shl(OFFSET, x2))
}
}
/**
* @dev Encodes a uint128 into a single bytes32 as the first uint128
* @param x1 The uint128
* @return z The encoded bytes32 as follows:
* [0 - 128[: x1
* [128 - 256[: empty
*/functionencodeFirst(uint128 x1) internalpurereturns (bytes32 z) {
assembly {
z :=and(x1, MASK_128)
}
}
/**
* @dev Encodes a uint128 into a single bytes32 as the second uint128
* @param x2 The uint128
* @return z The encoded bytes32 as follows:
* [0 - 128[: empty
* [128 - 256[: x2
*/functionencodeSecond(uint128 x2) internalpurereturns (bytes32 z) {
assembly {
z :=shl(OFFSET, x2)
}
}
/**
* @dev Encodes a uint128 into a single bytes32 as the first or second uint128
* @param x The uint128
* @param first Whether to encode as the first or second uint128
* @return z The encoded bytes32 as follows:
* if first:
* [0 - 128[: x
* [128 - 256[: empty
* else:
* [0 - 128[: empty
* [128 - 256[: x
*/functionencode(uint128 x, bool first) internalpurereturns (bytes32 z) {
return first ? encodeFirst(x) : encodeSecond(x);
}
/**
* @dev Decodes a bytes32 into two uint128
* @param z The encoded bytes32 as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @return x1 The first uint128
* @return x2 The second uint128
*/functiondecode(bytes32 z) internalpurereturns (uint128 x1, uint128 x2) {
assembly {
x1 :=and(z, MASK_128)
x2 :=shr(OFFSET, z)
}
}
/**
* @dev Decodes a bytes32 into a uint128 as the first uint128
* @param z The encoded bytes32 as follows:
* [0 - 128[: x
* [128 - 256[: any
* @return x The first uint128
*/functiondecodeX(bytes32 z) internalpurereturns (uint128 x) {
assembly {
x :=and(z, MASK_128)
}
}
/**
* @dev Decodes a bytes32 into a uint128 as the second uint128
* @param z The encoded bytes32 as follows:
* [0 - 128[: any
* [128 - 256[: y
* @return y The second uint128
*/functiondecodeY(bytes32 z) internalpurereturns (uint128 y) {
assembly {
y :=shr(OFFSET, z)
}
}
/**
* @dev Decodes a bytes32 into a uint128 as the first or second uint128
* @param z The encoded bytes32 as follows:
* if first:
* [0 - 128[: x1
* [128 - 256[: empty
* else:
* [0 - 128[: empty
* [128 - 256[: x2
* @param first Whether to decode as the first or second uint128
* @return x The decoded uint128
*/functiondecode(bytes32 z, bool first) internalpurereturns (uint128 x) {
return first ? decodeX(z) : decodeY(z);
}
/**
* @dev Adds two encoded bytes32, reverting on overflow on any of the uint128
* @param x The first bytes32 encoded as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @param y The second bytes32 encoded as follows:
* [0 - 128[: y1
* [128 - 256[: y2
* @return z The sum of x and y encoded as follows:
* [0 - 128[: x1 + y1
* [128 - 256[: x2 + y2
*/functionadd(bytes32 x, bytes32 y) internalpurereturns (bytes32 z) {
assembly {
z :=add(x, y)
}
if (z < x ||uint128(uint256(z)) <uint128(uint256(x))) {
revert PackedUint128Math__AddOverflow();
}
}
/**
* @dev Adds an encoded bytes32 and two uint128, reverting on overflow on any of the uint128
* @param x The bytes32 encoded as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @param y1 The first uint128
* @param y2 The second uint128
* @return z The sum of x and y encoded as follows:
* [0 - 128[: x1 + y1
* [128 - 256[: x2 + y2
*/functionadd(bytes32 x, uint128 y1, uint128 y2) internalpurereturns (bytes32) {
return add(x, encode(y1, y2));
}
/**
* @dev Subtracts two encoded bytes32, reverting on underflow on any of the uint128
* @param x The first bytes32 encoded as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @param y The second bytes32 encoded as follows:
* [0 - 128[: y1
* [128 - 256[: y2
* @return z The difference of x and y encoded as follows:
* [0 - 128[: x1 - y1
* [128 - 256[: x2 - y2
*/functionsub(bytes32 x, bytes32 y) internalpurereturns (bytes32 z) {
assembly {
z :=sub(x, y)
}
if (z > x ||uint128(uint256(z)) >uint128(uint256(x))) {
revert PackedUint128Math__SubUnderflow();
}
}
/**
* @dev Subtracts an encoded bytes32 and two uint128, reverting on underflow on any of the uint128
* @param x The bytes32 encoded as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @param y1 The first uint128
* @param y2 The second uint128
* @return z The difference of x and y encoded as follows:
* [0 - 128[: x1 - y1
* [128 - 256[: x2 - y2
*/functionsub(bytes32 x, uint128 y1, uint128 y2) internalpurereturns (bytes32) {
return sub(x, encode(y1, y2));
}
/**
* @dev Returns whether any of the uint128 of x is strictly greater than the corresponding uint128 of y
* @param x The first bytes32 encoded as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @param y The second bytes32 encoded as follows:
* [0 - 128[: y1
* [128 - 256[: y2
* @return x1 < y1 || x2 < y2
*/functionlt(bytes32 x, bytes32 y) internalpurereturns (bool) {
(uint128 x1, uint128 x2) = decode(x);
(uint128 y1, uint128 y2) = decode(y);
return x1 < y1 || x2 < y2;
}
/**
* @dev Returns whether any of the uint128 of x is strictly greater than the corresponding uint128 of y
* @param x The first bytes32 encoded as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @param y The second bytes32 encoded as follows:
* [0 - 128[: y1
* [128 - 256[: y2
* @return x1 < y1 || x2 < y2
*/functiongt(bytes32 x, bytes32 y) internalpurereturns (bool) {
(uint128 x1, uint128 x2) = decode(x);
(uint128 y1, uint128 y2) = decode(y);
return x1 > y1 || x2 > y2;
}
/**
* @dev Multiplies an encoded bytes32 by a uint128 then divides the result by 10_000, rounding down
* The result can't overflow as the multiplier needs to be smaller or equal to 10_000
* @param x The bytes32 encoded as follows:
* [0 - 128[: x1
* [128 - 256[: x2
* @param multiplier The uint128 to multiply by (must be smaller or equal to 10_000)
* @return z The product of x and multiplier encoded as follows:
* [0 - 128[: floor((x1 * multiplier) / 10_000)
* [128 - 256[: floor((x2 * multiplier) / 10_000)
*/functionscalarMulDivBasisPointRoundDown(bytes32 x, uint128 multiplier) internalpurereturns (bytes32 z) {
if (multiplier ==0) return0;
uint256 BASIS_POINT_MAX = Constants.BASIS_POINT_MAX;
if (multiplier > BASIS_POINT_MAX) revert PackedUint128Math__MultiplierTooLarge();
(uint128 x1, uint128 x2) = decode(x);
assembly {
x1 :=div(mul(x1, multiplier), BASIS_POINT_MAX)
x2 :=div(mul(x2, multiplier), BASIS_POINT_MAX)
}
return encode(x1, x2);
}
}
Contract Source Code
File 28 of 33: PairParameterHelper.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {Constants} from"./Constants.sol";
import {SafeCast} from"./math/SafeCast.sol";
import {Encoded} from"./math/Encoded.sol";
/**
* @title Liquidity Book Pair Parameter Helper Library
* @author Trader Joe
* @dev This library contains functions to get and set parameters of a pair
* The parameters are stored in a single bytes32 variable in the following format:
* [0 - 16[: base factor (16 bits)
* [16 - 28[: filter period (12 bits)
* [28 - 40[: decay period (12 bits)
* [40 - 54[: reduction factor (14 bits)
* [54 - 78[: variable fee control (24 bits)
* [78 - 92[: protocol share (14 bits)
* [92 - 112[: max volatility accumulator (20 bits)
* [112 - 132[: volatility accumulator (20 bits)
* [132 - 152[: volatility reference (20 bits)
* [152 - 176[: index reference (24 bits)
* [176 - 216[: time of last update (40 bits)
* [216 - 232[: oracle index (16 bits)
* [232 - 256[: active index (24 bits)
*/libraryPairParameterHelper{
usingSafeCastforuint256;
usingEncodedforbytes32;
errorPairParametersHelper__InvalidParameter();
uint256internalconstant OFFSET_BASE_FACTOR =0;
uint256internalconstant OFFSET_FILTER_PERIOD =16;
uint256internalconstant OFFSET_DECAY_PERIOD =28;
uint256internalconstant OFFSET_REDUCTION_FACTOR =40;
uint256internalconstant OFFSET_VAR_FEE_CONTROL =54;
uint256internalconstant OFFSET_PROTOCOL_SHARE =78;
uint256internalconstant OFFSET_MAX_VOL_ACC =92;
uint256internalconstant OFFSET_VOL_ACC =112;
uint256internalconstant OFFSET_VOL_REF =132;
uint256internalconstant OFFSET_ID_REF =152;
uint256internalconstant OFFSET_TIME_LAST_UPDATE =176;
uint256internalconstant OFFSET_ORACLE_ID =216;
uint256internalconstant OFFSET_ACTIVE_ID =232;
uint256internalconstant MASK_STATIC_PARAMETER =0xffffffffffffffffffffffffffff;
/**
* @dev Get the base factor from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 16[: base factor (16 bits)
* [16 - 256[: other parameters
* @return baseFactor The base factor
*/functiongetBaseFactor(bytes32 params) internalpurereturns (uint16 baseFactor) {
baseFactor = params.decodeUint16(OFFSET_BASE_FACTOR);
}
/**
* @dev Get the filter period from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 16[: other parameters
* [16 - 28[: filter period (12 bits)
* [28 - 256[: other parameters
* @return filterPeriod The filter period
*/functiongetFilterPeriod(bytes32 params) internalpurereturns (uint16 filterPeriod) {
filterPeriod = params.decodeUint12(OFFSET_FILTER_PERIOD);
}
/**
* @dev Get the decay period from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 28[: other parameters
* [28 - 40[: decay period (12 bits)
* [40 - 256[: other parameters
* @return decayPeriod The decay period
*/functiongetDecayPeriod(bytes32 params) internalpurereturns (uint16 decayPeriod) {
decayPeriod = params.decodeUint12(OFFSET_DECAY_PERIOD);
}
/**
* @dev Get the reduction factor from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 40[: other parameters
* [40 - 54[: reduction factor (14 bits)
* [54 - 256[: other parameters
* @return reductionFactor The reduction factor
*/functiongetReductionFactor(bytes32 params) internalpurereturns (uint16 reductionFactor) {
reductionFactor = params.decodeUint14(OFFSET_REDUCTION_FACTOR);
}
/**
* @dev Get the variable fee control from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 54[: other parameters
* [54 - 78[: variable fee control (24 bits)
* [78 - 256[: other parameters
* @return variableFeeControl The variable fee control
*/functiongetVariableFeeControl(bytes32 params) internalpurereturns (uint24 variableFeeControl) {
variableFeeControl = params.decodeUint24(OFFSET_VAR_FEE_CONTROL);
}
/**
* @dev Get the protocol share from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 78[: other parameters
* [78 - 92[: protocol share (14 bits)
* [92 - 256[: other parameters
* @return protocolShare The protocol share
*/functiongetProtocolShare(bytes32 params) internalpurereturns (uint16 protocolShare) {
protocolShare = params.decodeUint14(OFFSET_PROTOCOL_SHARE);
}
/**
* @dev Get the max volatility accumulator from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 92[: other parameters
* [92 - 112[: max volatility accumulator (20 bits)
* [112 - 256[: other parameters
* @return maxVolatilityAccumulator The max volatility accumulator
*/functiongetMaxVolatilityAccumulator(bytes32 params) internalpurereturns (uint24 maxVolatilityAccumulator) {
maxVolatilityAccumulator = params.decodeUint20(OFFSET_MAX_VOL_ACC);
}
/**
* @dev Get the volatility accumulator from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 112[: other parameters
* [112 - 132[: volatility accumulator (20 bits)
* [132 - 256[: other parameters
* @return volatilityAccumulator The volatility accumulator
*/functiongetVolatilityAccumulator(bytes32 params) internalpurereturns (uint24 volatilityAccumulator) {
volatilityAccumulator = params.decodeUint20(OFFSET_VOL_ACC);
}
/**
* @dev Get the volatility reference from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 132[: other parameters
* [132 - 152[: volatility reference (20 bits)
* [152 - 256[: other parameters
* @return volatilityReference The volatility reference
*/functiongetVolatilityReference(bytes32 params) internalpurereturns (uint24 volatilityReference) {
volatilityReference = params.decodeUint20(OFFSET_VOL_REF);
}
/**
* @dev Get the index reference from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 152[: other parameters
* [152 - 176[: index reference (24 bits)
* [176 - 256[: other parameters
* @return idReference The index reference
*/functiongetIdReference(bytes32 params) internalpurereturns (uint24 idReference) {
idReference = params.decodeUint24(OFFSET_ID_REF);
}
/**
* @dev Get the time of last update from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 176[: other parameters
* [176 - 216[: time of last update (40 bits)
* [216 - 256[: other parameters
* @return timeOflastUpdate The time of last update
*/functiongetTimeOfLastUpdate(bytes32 params) internalpurereturns (uint40 timeOflastUpdate) {
timeOflastUpdate = params.decodeUint40(OFFSET_TIME_LAST_UPDATE);
}
/**
* @dev Get the oracle id from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 216[: other parameters
* [216 - 232[: oracle id (16 bits)
* [232 - 256[: other parameters
* @return oracleId The oracle id
*/functiongetOracleId(bytes32 params) internalpurereturns (uint16 oracleId) {
oracleId = params.decodeUint16(OFFSET_ORACLE_ID);
}
/**
* @dev Get the active index from the encoded pair parameters
* @param params The encoded pair parameters, as follows:
* [0 - 232[: other parameters
* [232 - 256[: active index (24 bits)
* @return activeId The active index
*/functiongetActiveId(bytes32 params) internalpurereturns (uint24 activeId) {
activeId = params.decodeUint24(OFFSET_ACTIVE_ID);
}
/**
* @dev Get the delta between the current active index and the cached active index
* @param params The encoded pair parameters, as follows:
* [0 - 232[: other parameters
* [232 - 256[: active index (24 bits)
* @param activeId The current active index
* @return The delta
*/functiongetDeltaId(bytes32 params, uint24 activeId) internalpurereturns (uint24) {
uint24 id = getActiveId(params);
unchecked {
return activeId > id ? activeId - id : id - activeId;
}
}
/**
* @dev Calculates the base fee, with 18 decimals
* @param params The encoded pair parameters
* @param binStep The bin step (in basis points)
* @return baseFee The base fee
*/functiongetBaseFee(bytes32 params, uint16 binStep) internalpurereturns (uint256) {
unchecked {
// Base factor is in basis points, binStep is in basis points, so we multiply by 1e10returnuint256(getBaseFactor(params)) * binStep *1e10;
}
}
/**
* @dev Calculates the variable fee
* @param params The encoded pair parameters
* @param binStep The bin step (in basis points)
* @return variableFee The variable fee
*/functiongetVariableFee(bytes32 params, uint16 binStep) internalpurereturns (uint256 variableFee) {
uint256 variableFeeControl = getVariableFeeControl(params);
if (variableFeeControl !=0) {
unchecked {
// The volatility accumulator is in basis points, binStep is in basis points,// and the variable fee control is in basis points, so the result is in 100e18thuint256 prod =uint256(getVolatilityAccumulator(params)) * binStep;
variableFee = (prod * prod * variableFeeControl +99) /100;
}
}
}
/**
* @dev Calculates the total fee, which is the sum of the base fee and the variable fee
* @param params The encoded pair parameters
* @param binStep The bin step (in basis points)
* @return totalFee The total fee
*/functiongetTotalFee(bytes32 params, uint16 binStep) internalpurereturns (uint128) {
unchecked {
return (getBaseFee(params, binStep) + getVariableFee(params, binStep)).safe128();
}
}
/**
* @dev Set the oracle id in the encoded pair parameters
* @param params The encoded pair parameters
* @param oracleId The oracle id
* @return The updated encoded pair parameters
*/functionsetOracleId(bytes32 params, uint16 oracleId) internalpurereturns (bytes32) {
return params.set(oracleId, Encoded.MASK_UINT16, OFFSET_ORACLE_ID);
}
/**
* @dev Set the volatility reference in the encoded pair parameters
* @param params The encoded pair parameters
* @param volRef The volatility reference
* @return The updated encoded pair parameters
*/functionsetVolatilityReference(bytes32 params, uint24 volRef) internalpurereturns (bytes32) {
if (volRef > Encoded.MASK_UINT20) revert PairParametersHelper__InvalidParameter();
return params.set(volRef, Encoded.MASK_UINT20, OFFSET_VOL_REF);
}
/**
* @dev Set the volatility accumulator in the encoded pair parameters
* @param params The encoded pair parameters
* @param volAcc The volatility accumulator
* @return The updated encoded pair parameters
*/functionsetVolatilityAccumulator(bytes32 params, uint24 volAcc) internalpurereturns (bytes32) {
if (volAcc > Encoded.MASK_UINT20) revert PairParametersHelper__InvalidParameter();
return params.set(volAcc, Encoded.MASK_UINT20, OFFSET_VOL_ACC);
}
/**
* @dev Set the active id in the encoded pair parameters
* @param params The encoded pair parameters
* @param activeId The active id
* @return newParams The updated encoded pair parameters
*/functionsetActiveId(bytes32 params, uint24 activeId) internalpurereturns (bytes32 newParams) {
return params.set(activeId, Encoded.MASK_UINT24, OFFSET_ACTIVE_ID);
}
/**
* @dev Sets the static fee parameters in the encoded pair parameters
* @param params The encoded pair parameters
* @param baseFactor The base factor
* @param filterPeriod The filter period
* @param decayPeriod The decay period
* @param reductionFactor The reduction factor
* @param variableFeeControl The variable fee control
* @param protocolShare The protocol share
* @param maxVolatilityAccumulator The max volatility accumulator
* @return newParams The updated encoded pair parameters
*/functionsetStaticFeeParameters(bytes32 params,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) internalpurereturns (bytes32 newParams) {
if (
filterPeriod > decayPeriod || decayPeriod > Encoded.MASK_UINT12
|| reductionFactor > Constants.BASIS_POINT_MAX || protocolShare > Constants.MAX_PROTOCOL_SHARE
|| maxVolatilityAccumulator > Encoded.MASK_UINT20
) revert PairParametersHelper__InvalidParameter();
newParams = newParams.set(baseFactor, Encoded.MASK_UINT16, OFFSET_BASE_FACTOR);
newParams = newParams.set(filterPeriod, Encoded.MASK_UINT12, OFFSET_FILTER_PERIOD);
newParams = newParams.set(decayPeriod, Encoded.MASK_UINT12, OFFSET_DECAY_PERIOD);
newParams = newParams.set(reductionFactor, Encoded.MASK_UINT14, OFFSET_REDUCTION_FACTOR);
newParams = newParams.set(variableFeeControl, Encoded.MASK_UINT24, OFFSET_VAR_FEE_CONTROL);
newParams = newParams.set(protocolShare, Encoded.MASK_UINT14, OFFSET_PROTOCOL_SHARE);
newParams = newParams.set(maxVolatilityAccumulator, Encoded.MASK_UINT20, OFFSET_MAX_VOL_ACC);
return params.set(uint256(newParams), MASK_STATIC_PARAMETER, 0);
}
/**
* @dev Updates the index reference in the encoded pair parameters
* @param params The encoded pair parameters
* @return newParams The updated encoded pair parameters
*/functionupdateIdReference(bytes32 params) internalpurereturns (bytes32 newParams) {
uint24 activeId = getActiveId(params);
return params.set(activeId, Encoded.MASK_UINT24, OFFSET_ID_REF);
}
/**
* @dev Updates the time of last update in the encoded pair parameters
* @param params The encoded pair parameters
* @param timestamp The timestamp
* @return newParams The updated encoded pair parameters
*/functionupdateTimeOfLastUpdate(bytes32 params, uint256 timestamp) internalpurereturns (bytes32 newParams) {
uint40 currentTime = timestamp.safe40();
return params.set(currentTime, Encoded.MASK_UINT40, OFFSET_TIME_LAST_UPDATE);
}
/**
* @dev Updates the volatility reference in the encoded pair parameters
* @param params The encoded pair parameters
* @return The updated encoded pair parameters
*/functionupdateVolatilityReference(bytes32 params) internalpurereturns (bytes32) {
uint256 volAcc = getVolatilityAccumulator(params);
uint256 reductionFactor = getReductionFactor(params);
uint24 volRef;
unchecked {
volRef =uint24(volAcc * reductionFactor / Constants.BASIS_POINT_MAX);
}
return setVolatilityReference(params, volRef);
}
/**
* @dev Updates the volatility accumulator in the encoded pair parameters
* @param params The encoded pair parameters
* @param activeId The active id
* @return The updated encoded pair parameters
*/functionupdateVolatilityAccumulator(bytes32 params, uint24 activeId) internalpurereturns (bytes32) {
uint256 idReference = getIdReference(params);
uint256 deltaId;
uint256 volAcc;
unchecked {
deltaId = activeId > idReference ? activeId - idReference : idReference - activeId;
volAcc = (uint256(getVolatilityReference(params)) + deltaId * Constants.BASIS_POINT_MAX);
}
uint256 maxVolAcc = getMaxVolatilityAccumulator(params);
volAcc = volAcc > maxVolAcc ? maxVolAcc : volAcc;
return setVolatilityAccumulator(params, uint24(volAcc));
}
/**
* @dev Updates the volatility reference and the volatility accumulator in the encoded pair parameters
* @param params The encoded pair parameters
* @param timestamp The timestamp
* @return The updated encoded pair parameters
*/functionupdateReferences(bytes32 params, uint256 timestamp) internalpurereturns (bytes32) {
uint256 dt = timestamp - getTimeOfLastUpdate(params);
if (dt >= getFilterPeriod(params)) {
params = updateIdReference(params);
params = dt < getDecayPeriod(params) ? updateVolatilityReference(params) : setVolatilityReference(params, 0);
}
return updateTimeOfLastUpdate(params, timestamp);
}
/**
* @dev Updates the volatility reference and the volatility accumulator in the encoded pair parameters
* @param params The encoded pair parameters
* @param activeId The active id
* @param timestamp The timestamp
* @return The updated encoded pair parameters
*/functionupdateVolatilityParameters(bytes32 params, uint24 activeId, uint256 timestamp)
internalpurereturns (bytes32)
{
params = updateReferences(params, timestamp);
return updateVolatilityAccumulator(params, activeId);
}
}
Contract Source Code
File 29 of 33: PriceHelper.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {Uint128x128Math} from"./math/Uint128x128Math.sol";
import {Uint256x256Math} from"./math/Uint256x256Math.sol";
import {SafeCast} from"./math/SafeCast.sol";
import {Constants} from"./Constants.sol";
/**
* @title Liquidity Book Price Helper Library
* @author Trader Joe
* @notice This library contains functions to calculate prices
*/libraryPriceHelper{
usingUint128x128Mathforuint256;
usingUint256x256Mathforuint256;
usingSafeCastforuint256;
int256privateconstant REAL_ID_SHIFT =1<<23;
/**
* @dev Calculates the price from the id and the bin step
* @param id The id
* @param binStep The bin step
* @return price The price as a 128.128-binary fixed-point number
*/functiongetPriceFromId(uint24 id, uint16 binStep) internalpurereturns (uint256 price) {
uint256 base = getBase(binStep);
int256 exponent = getExponent(id);
price = base.pow(exponent);
}
/**
* @dev Calculates the id from the price and the bin step
* @param price The price as a 128.128-binary fixed-point number
* @param binStep The bin step
* @return id The id
*/functiongetIdFromPrice(uint256 price, uint16 binStep) internalpurereturns (uint24 id) {
uint256 base = getBase(binStep);
int256 realId = price.log2() / base.log2();
unchecked {
id =uint256(REAL_ID_SHIFT + realId).safe24();
}
}
/**
* @dev Calculates the base from the bin step, which is `1 + binStep / BASIS_POINT_MAX`
* @param binStep The bin step
* @return base The base
*/functiongetBase(uint16 binStep) internalpurereturns (uint256) {
unchecked {
return Constants.SCALE + (uint256(binStep) << Constants.SCALE_OFFSET) / Constants.BASIS_POINT_MAX;
}
}
/**
* @dev Calculates the exponent from the id, which is `id - REAL_ID_SHIFT`
* @param id The id
* @return exponent The exponent
*/functiongetExponent(uint24 id) internalpurereturns (int256) {
unchecked {
returnint256(uint256(id)) - REAL_ID_SHIFT;
}
}
/**
* @dev Converts a price with 18 decimals to a 128.128-binary fixed-point number
* @param price The price with 18 decimals
* @return price128x128 The 128.128-binary fixed-point number
*/functionconvertDecimalPriceTo128x128(uint256 price) internalpurereturns (uint256) {
return price.shiftDivRoundDown(Constants.SCALE_OFFSET, Constants.PRECISION);
}
/**
* @dev Converts a 128.128-binary fixed-point number to a price with 18 decimals
* @param price128x128 The 128.128-binary fixed-point number
* @return price The price with 18 decimals
*/functionconvert128x128PriceToDecimal(uint256 price128x128) internalpurereturns (uint256) {
return price128x128.mulShiftRoundDown(Constants.PRECISION, Constants.SCALE_OFFSET);
}
}
Contract Source Code
File 30 of 33: SafeCast.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;/**
* @title Liquidity Book Safe Cast Library
* @author Trader Joe
* @notice This library contains functions to safely cast uint256 to different uint types.
*/librarySafeCast{
errorSafeCast__Exceeds248Bits();
errorSafeCast__Exceeds240Bits();
errorSafeCast__Exceeds232Bits();
errorSafeCast__Exceeds224Bits();
errorSafeCast__Exceeds216Bits();
errorSafeCast__Exceeds208Bits();
errorSafeCast__Exceeds200Bits();
errorSafeCast__Exceeds192Bits();
errorSafeCast__Exceeds184Bits();
errorSafeCast__Exceeds176Bits();
errorSafeCast__Exceeds168Bits();
errorSafeCast__Exceeds160Bits();
errorSafeCast__Exceeds152Bits();
errorSafeCast__Exceeds144Bits();
errorSafeCast__Exceeds136Bits();
errorSafeCast__Exceeds128Bits();
errorSafeCast__Exceeds120Bits();
errorSafeCast__Exceeds112Bits();
errorSafeCast__Exceeds104Bits();
errorSafeCast__Exceeds96Bits();
errorSafeCast__Exceeds88Bits();
errorSafeCast__Exceeds80Bits();
errorSafeCast__Exceeds72Bits();
errorSafeCast__Exceeds64Bits();
errorSafeCast__Exceeds56Bits();
errorSafeCast__Exceeds48Bits();
errorSafeCast__Exceeds40Bits();
errorSafeCast__Exceeds32Bits();
errorSafeCast__Exceeds24Bits();
errorSafeCast__Exceeds16Bits();
errorSafeCast__Exceeds8Bits();
/**
* @dev Returns x on uint248 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint248
*/functionsafe248(uint256 x) internalpurereturns (uint248 y) {
if ((y =uint248(x)) != x) revert SafeCast__Exceeds248Bits();
}
/**
* @dev Returns x on uint240 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint240
*/functionsafe240(uint256 x) internalpurereturns (uint240 y) {
if ((y =uint240(x)) != x) revert SafeCast__Exceeds240Bits();
}
/**
* @dev Returns x on uint232 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint232
*/functionsafe232(uint256 x) internalpurereturns (uint232 y) {
if ((y =uint232(x)) != x) revert SafeCast__Exceeds232Bits();
}
/**
* @dev Returns x on uint224 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint224
*/functionsafe224(uint256 x) internalpurereturns (uint224 y) {
if ((y =uint224(x)) != x) revert SafeCast__Exceeds224Bits();
}
/**
* @dev Returns x on uint216 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint216
*/functionsafe216(uint256 x) internalpurereturns (uint216 y) {
if ((y =uint216(x)) != x) revert SafeCast__Exceeds216Bits();
}
/**
* @dev Returns x on uint208 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint208
*/functionsafe208(uint256 x) internalpurereturns (uint208 y) {
if ((y =uint208(x)) != x) revert SafeCast__Exceeds208Bits();
}
/**
* @dev Returns x on uint200 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint200
*/functionsafe200(uint256 x) internalpurereturns (uint200 y) {
if ((y =uint200(x)) != x) revert SafeCast__Exceeds200Bits();
}
/**
* @dev Returns x on uint192 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint192
*/functionsafe192(uint256 x) internalpurereturns (uint192 y) {
if ((y =uint192(x)) != x) revert SafeCast__Exceeds192Bits();
}
/**
* @dev Returns x on uint184 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint184
*/functionsafe184(uint256 x) internalpurereturns (uint184 y) {
if ((y =uint184(x)) != x) revert SafeCast__Exceeds184Bits();
}
/**
* @dev Returns x on uint176 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint176
*/functionsafe176(uint256 x) internalpurereturns (uint176 y) {
if ((y =uint176(x)) != x) revert SafeCast__Exceeds176Bits();
}
/**
* @dev Returns x on uint168 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint168
*/functionsafe168(uint256 x) internalpurereturns (uint168 y) {
if ((y =uint168(x)) != x) revert SafeCast__Exceeds168Bits();
}
/**
* @dev Returns x on uint160 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint160
*/functionsafe160(uint256 x) internalpurereturns (uint160 y) {
if ((y =uint160(x)) != x) revert SafeCast__Exceeds160Bits();
}
/**
* @dev Returns x on uint152 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint152
*/functionsafe152(uint256 x) internalpurereturns (uint152 y) {
if ((y =uint152(x)) != x) revert SafeCast__Exceeds152Bits();
}
/**
* @dev Returns x on uint144 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint144
*/functionsafe144(uint256 x) internalpurereturns (uint144 y) {
if ((y =uint144(x)) != x) revert SafeCast__Exceeds144Bits();
}
/**
* @dev Returns x on uint136 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint136
*/functionsafe136(uint256 x) internalpurereturns (uint136 y) {
if ((y =uint136(x)) != x) revert SafeCast__Exceeds136Bits();
}
/**
* @dev Returns x on uint128 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint128
*/functionsafe128(uint256 x) internalpurereturns (uint128 y) {
if ((y =uint128(x)) != x) revert SafeCast__Exceeds128Bits();
}
/**
* @dev Returns x on uint120 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint120
*/functionsafe120(uint256 x) internalpurereturns (uint120 y) {
if ((y =uint120(x)) != x) revert SafeCast__Exceeds120Bits();
}
/**
* @dev Returns x on uint112 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint112
*/functionsafe112(uint256 x) internalpurereturns (uint112 y) {
if ((y =uint112(x)) != x) revert SafeCast__Exceeds112Bits();
}
/**
* @dev Returns x on uint104 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint104
*/functionsafe104(uint256 x) internalpurereturns (uint104 y) {
if ((y =uint104(x)) != x) revert SafeCast__Exceeds104Bits();
}
/**
* @dev Returns x on uint96 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint96
*/functionsafe96(uint256 x) internalpurereturns (uint96 y) {
if ((y =uint96(x)) != x) revert SafeCast__Exceeds96Bits();
}
/**
* @dev Returns x on uint88 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint88
*/functionsafe88(uint256 x) internalpurereturns (uint88 y) {
if ((y =uint88(x)) != x) revert SafeCast__Exceeds88Bits();
}
/**
* @dev Returns x on uint80 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint80
*/functionsafe80(uint256 x) internalpurereturns (uint80 y) {
if ((y =uint80(x)) != x) revert SafeCast__Exceeds80Bits();
}
/**
* @dev Returns x on uint72 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint72
*/functionsafe72(uint256 x) internalpurereturns (uint72 y) {
if ((y =uint72(x)) != x) revert SafeCast__Exceeds72Bits();
}
/**
* @dev Returns x on uint64 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint64
*/functionsafe64(uint256 x) internalpurereturns (uint64 y) {
if ((y =uint64(x)) != x) revert SafeCast__Exceeds64Bits();
}
/**
* @dev Returns x on uint56 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint56
*/functionsafe56(uint256 x) internalpurereturns (uint56 y) {
if ((y =uint56(x)) != x) revert SafeCast__Exceeds56Bits();
}
/**
* @dev Returns x on uint48 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint48
*/functionsafe48(uint256 x) internalpurereturns (uint48 y) {
if ((y =uint48(x)) != x) revert SafeCast__Exceeds48Bits();
}
/**
* @dev Returns x on uint40 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint40
*/functionsafe40(uint256 x) internalpurereturns (uint40 y) {
if ((y =uint40(x)) != x) revert SafeCast__Exceeds40Bits();
}
/**
* @dev Returns x on uint32 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint32
*/functionsafe32(uint256 x) internalpurereturns (uint32 y) {
if ((y =uint32(x)) != x) revert SafeCast__Exceeds32Bits();
}
/**
* @dev Returns x on uint24 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint24
*/functionsafe24(uint256 x) internalpurereturns (uint24 y) {
if ((y =uint24(x)) != x) revert SafeCast__Exceeds24Bits();
}
/**
* @dev Returns x on uint16 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint16
*/functionsafe16(uint256 x) internalpurereturns (uint16 y) {
if ((y =uint16(x)) != x) revert SafeCast__Exceeds16Bits();
}
/**
* @dev Returns x on uint8 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint8
*/functionsafe8(uint256 x) internalpurereturns (uint8 y) {
if ((y =uint8(x)) != x) revert SafeCast__Exceeds8Bits();
}
}
Contract Source Code
File 31 of 33: TokenHelper.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {IERC20} from"@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title Liquidity Book Token Helper Library
* @author Trader Joe
* @notice Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using TokenHelper for IERC20;` statement to your contract,
* which allows you to call the safe operation as `token.safeTransfer(...)`
*/libraryTokenHelper{
errorTokenHelper__TransferFailed();
/**
* @notice Transfers token and reverts if the transfer fails
* @param token The address of the token
* @param owner The owner of the tokens
* @param recipient The address of the recipient
* @param amount The amount to send
*/functionsafeTransferFrom(IERC20 token, address owner, address recipient, uint256 amount) internal{
bytesmemory data =abi.encodeWithSelector(token.transferFrom.selector, owner, recipient, amount);
_callAndCatch(token, data);
}
/**
* @notice Transfers token and reverts if the transfer fails
* @param token The address of the token
* @param recipient The address of the recipient
* @param amount The amount to send
*/functionsafeTransfer(IERC20 token, address recipient, uint256 amount) internal{
bytesmemory data =abi.encodeWithSelector(token.transfer.selector, recipient, amount);
_callAndCatch(token, data);
}
function_callAndCatch(IERC20 token, bytesmemory data) internal{
bool success;
assembly {
mstore(0x00, 0)
success :=call(gas(), token, 0, add(data, 0x20), mload(data), 0x00, 0x20)
switch success
case0 {
ifreturndatasize() {
returndatacopy(0x00, 0x00, returndatasize())
revert(0x00, returndatasize())
}
}
default {
switchreturndatasize()
case0 { success :=iszero(iszero(extcodesize(token))) }
default { success :=and(success, eq(mload(0x00), 1)) }
}
}
if (!success) revert TokenHelper__TransferFailed();
}
}
Contract Source Code
File 32 of 33: Uint128x128Math.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {Constants} from"../Constants.sol";
import {BitMath} from"./BitMath.sol";
/**
* @title Liquidity Book Uint128x128 Math Library
* @author Trader Joe
* @notice Helper contract used for power and log calculations
*/libraryUint128x128Math{
usingBitMathforuint256;
errorUint128x128Math__LogUnderflow();
errorUint128x128Math__PowUnderflow(uint256 x, int256 y);
uint256constant LOG_SCALE_OFFSET =127;
uint256constant LOG_SCALE =1<< LOG_SCALE_OFFSET;
uint256constant LOG_SCALE_SQUARED = LOG_SCALE * LOG_SCALE;
/**
* @notice Calculates the binary logarithm of x.
* @dev Based on the iterative approximation algorithm.
* https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
* Requirements:
* - x must be greater than zero.
* Caveats:
* - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation
* Also because x is converted to an unsigned 129.127-binary fixed-point number during the operation to optimize the multiplication
* @param x The unsigned 128.128-binary fixed-point number for which to calculate the binary logarithm.
* @return result The binary logarithm as a signed 128.128-binary fixed-point number.
*/functionlog2(uint256 x) internalpurereturns (int256 result) {
// Convert x to a unsigned 129.127-binary fixed-point number to optimize the multiplication.// If we use an offset of 128 bits, y would need 129 bits and y**2 would would overflow and we would have to// use mulDiv, by reducing x to 129.127-binary fixed-point number we assert that y will use 128 bits, and we// can use the regular multiplicationif (x ==1) return-128;
if (x ==0) revert Uint128x128Math__LogUnderflow();
x >>=1;
unchecked {
// This works because log2(x) = -log2(1/x).int256 sign;
if (x >= LOG_SCALE) {
sign =1;
} else {
sign =-1;
// Do the fixed-point inversion inline to save gas
x = LOG_SCALE_SQUARED / x;
}
// Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).uint256 n = (x >> LOG_SCALE_OFFSET).mostSignificantBit();
// The integer part of the logarithm as a signed 129.127-binary fixed-point number. The operation can't overflow// because n is maximum 255, LOG_SCALE_OFFSET is 127 bits and sign is either 1 or -1.
result =int256(n) << LOG_SCALE_OFFSET;
// This is y = x * 2^(-n).uint256 y = x >> n;
// If y = 1, the fractional part is zero.if (y != LOG_SCALE) {
// Calculate the fractional part via the iterative approximation.// The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.for (int256 delta =int256(1<< (LOG_SCALE_OFFSET -1)); delta >0; delta >>=1) {
y = (y * y) >> LOG_SCALE_OFFSET;
// Is y^2 > 2 and so in the range [2,4)?if (y >=1<< (LOG_SCALE_OFFSET +1)) {
// Add the 2^(-m) factor to the logarithm.
result += delta;
// Corresponds to z/2 on Wikipedia.
y >>=1;
}
}
}
// Convert x back to unsigned 128.128-binary fixed-point number
result = (result * sign) <<1;
}
}
/**
* @notice Returns the value of x^y. It calculates `1 / x^abs(y)` if x is bigger than 2^128.
* At the end of the operations, we invert the result if needed.
* @param x The unsigned 128.128-binary fixed-point number for which to calculate the power
* @param y A relative number without any decimals, needs to be between ]-2^21; 2^21[
*/functionpow(uint256 x, int256 y) internalpurereturns (uint256 result) {
bool invert;
uint256 absY;
if (y ==0) return Constants.SCALE;
assembly {
absY := y
ifslt(absY, 0) {
absY :=sub(0, absY)
invert :=iszero(invert)
}
}
if (absY <0x100000) {
result = Constants.SCALE;
assembly {
let squared := x
ifgt(x, 0xffffffffffffffffffffffffffffffff) {
squared :=div(not(0), squared)
invert :=iszero(invert)
}
ifand(absY, 0x1) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x2) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x4) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x8) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x10) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x20) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x40) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x80) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x100) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x200) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x400) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x800) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x1000) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x2000) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x4000) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x8000) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x10000) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x20000) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x40000) { result :=shr(128, mul(result, squared)) }
squared :=shr(128, mul(squared, squared))
ifand(absY, 0x80000) { result :=shr(128, mul(result, squared)) }
}
}
// revert if y is too big or if x^y underflowedif (result ==0) revert Uint128x128Math__PowUnderflow(x, y);
return invert ? type(uint256).max/ result : result;
}
}
Contract Source Code
File 33 of 33: Uint256x256Math.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.10;import {BitMath} from"./BitMath.sol";
/**
* @title Liquidity Book Uint256x256 Math Library
* @author Trader Joe
* @notice Helper contract used for full precision calculations
*/libraryUint256x256Math{
errorUint256x256Math__MulShiftOverflow();
errorUint256x256Math__MulDivOverflow();
/**
* @notice Calculates floor(x*y/denominator) with full precision
* The result will be rounded down
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The denominator cannot be zero
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param denominator The divisor as an uint256
* @return result The result as an uint256
*/functionmulDivRoundDown(uint256 x, uint256 y, uint256 denominator) internalpurereturns (uint256 result) {
(uint256 prod0, uint256 prod1) = _getMulProds(x, y);
return _getEndOfDivRoundDown(x, y, denominator, prod0, prod1);
}
/**
* @notice Calculates ceil(x*y/denominator) with full precision
* The result will be rounded up
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The denominator cannot be zero
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param denominator The divisor as an uint256
* @return result The result as an uint256
*/functionmulDivRoundUp(uint256 x, uint256 y, uint256 denominator) internalpurereturns (uint256 result) {
result = mulDivRoundDown(x, y, denominator);
if (mulmod(x, y, denominator) !=0) result +=1;
}
/**
* @notice Calculates floor(x * y / 2**offset) with full precision
* The result will be rounded down
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The offset needs to be strictly lower than 256
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param offset The offset as an uint256, can't be greater than 256
* @return result The result as an uint256
*/functionmulShiftRoundDown(uint256 x, uint256 y, uint8 offset) internalpurereturns (uint256 result) {
(uint256 prod0, uint256 prod1) = _getMulProds(x, y);
if (prod0 !=0) result = prod0 >> offset;
if (prod1 !=0) {
// Make sure the result is less than 2^256.if (prod1 >=1<< offset) revert Uint256x256Math__MulShiftOverflow();
unchecked {
result += prod1 << (256- offset);
}
}
}
/**
* @notice Calculates floor(x * y / 2**offset) with full precision
* The result will be rounded down
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The offset needs to be strictly lower than 256
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param offset The offset as an uint256, can't be greater than 256
* @return result The result as an uint256
*/functionmulShiftRoundUp(uint256 x, uint256 y, uint8 offset) internalpurereturns (uint256 result) {
result = mulShiftRoundDown(x, y, offset);
if (mulmod(x, y, 1<< offset) !=0) result +=1;
}
/**
* @notice Calculates floor(x << offset / y) with full precision
* The result will be rounded down
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The offset needs to be strictly lower than 256
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param offset The number of bit to shift x as an uint256
* @param denominator The divisor as an uint256
* @return result The result as an uint256
*/functionshiftDivRoundDown(uint256 x, uint8 offset, uint256 denominator) internalpurereturns (uint256 result) {
uint256 prod0;
uint256 prod1;
prod0 = x << offset; // Least significant 256 bits of the productunchecked {
prod1 = x >> (256- offset); // Most significant 256 bits of the product
}
return _getEndOfDivRoundDown(x, 1<< offset, denominator, prod0, prod1);
}
/**
* @notice Calculates ceil(x << offset / y) with full precision
* The result will be rounded up
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The offset needs to be strictly lower than 256
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param offset The number of bit to shift x as an uint256
* @param denominator The divisor as an uint256
* @return result The result as an uint256
*/functionshiftDivRoundUp(uint256 x, uint8 offset, uint256 denominator) internalpurereturns (uint256 result) {
result = shiftDivRoundDown(x, offset, denominator);
if (mulmod(x, 1<< offset, denominator) !=0) result +=1;
}
/**
* @notice Helper function to return the result of `x * y` as 2 uint256
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @return prod0 The least significant 256 bits of the product
* @return prod1 The most significant 256 bits of the product
*/function_getMulProds(uint256 x, uint256 y) privatepurereturns (uint256 prod0, uint256 prod1) {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256// variables such that product = prod1 * 2^256 + prod0.assembly {
let mm :=mulmod(x, y, not(0))
prod0 :=mul(x, y)
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
}
/**
* @notice Helper function to return the result of `x * y / denominator` with full precision
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param denominator The divisor as an uint256
* @param prod0 The least significant 256 bits of the product
* @param prod1 The most significant 256 bits of the product
* @return result The result as an uint256
*/function_getEndOfDivRoundDown(uint256 x, uint256 y, uint256 denominator, uint256 prod0, uint256 prod1)
privatepurereturns (uint256 result)
{
// Handle non-overflow cases, 256 by 256 divisionif (prod1 ==0) {
unchecked {
result = prod0 / denominator;
}
} else {
// Make sure the result is less than 2^256. Also prevents denominator == 0if (prod1 >= denominator) revert Uint256x256Math__MulDivOverflow();
// Make division exact by subtracting the remainder from [prod1 prod0].uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1// See https://cs.stackexchange.com/q/138556/92363unchecked {
// Does not overflow because the denominator cannot be zero at this stage in the functionuint256 lpotdod = denominator & (~denominator +1);
assembly {
// Divide denominator by lpotdod.
denominator :=div(denominator, lpotdod)
// Divide [prod1 prod0] by lpotdod.
prod0 :=div(prod0, lpotdod)
// Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one
lpotdod :=add(div(sub(0, lpotdod), lpotdod), 1)
}
// Shift in bits from prod1 into prod0
prod0 |= prod1 * lpotdod;
// 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 for// four bits. That is, denominator * inv = 1 mod 2^4uint256 inverse = (3* denominator) ^2;
// Use the 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
inverse *=2- denominator * inverse; // inverse mod 2^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // 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 * inverse;
}
}
}
/**
* @notice Calculates the square root of x
* @dev Credit to OpenZeppelin's Math library under MIT license
*/functionsqrt(uint256 x) internalpurereturns (uint256 sqrtX) {
if (x ==0) return0;
uint256 msb = BitMath.mostSignificantBit(x);
assembly {
sqrtX :=shl(shr(1, msb), 1)
sqrtX :=shr(1, add(sqrtX, div(x, sqrtX)))
sqrtX :=shr(1, add(sqrtX, div(x, sqrtX)))
sqrtX :=shr(1, add(sqrtX, div(x, sqrtX)))
sqrtX :=shr(1, add(sqrtX, div(x, sqrtX)))
sqrtX :=shr(1, add(sqrtX, div(x, sqrtX)))
sqrtX :=shr(1, add(sqrtX, div(x, sqrtX)))
sqrtX :=shr(1, add(sqrtX, div(x, sqrtX)))
x :=div(x, sqrtX)
}
return sqrtX < x ? sqrtX : x;
}
}