Source Code
Overview
MON Balance
MON Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
LBPair
Compiler Version
v0.8.20+commit.a1b79de6
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {BinHelper} from "./libraries/BinHelper.sol";
import {Clone} from "./libraries/Clone.sol";
import {Constants} from "./libraries/Constants.sol";
import {FeeHelper} from "./libraries/FeeHelper.sol";
import {LiquidityConfigurations} from "./libraries/math/LiquidityConfigurations.sol";
import {ReentrancyGuardUpgradeable} from "./libraries/ReentrancyGuardUpgradeable.sol";
import {ILBFactory} from "./interfaces/ILBFactory.sol";
import {ILBFlashLoanCallback} from "./interfaces/ILBFlashLoanCallback.sol";
import {ILBPair} from "./interfaces/ILBPair.sol";
import {LBToken, ILBToken} from "./LBToken.sol";
import {OracleHelper} from "./libraries/OracleHelper.sol";
import {PackedUint128Math} from "./libraries/math/PackedUint128Math.sol";
import {PairParameterHelper} from "./libraries/PairParameterHelper.sol";
import {PriceHelper} from "./libraries/PriceHelper.sol";
import {SafeCast} from "./libraries/math/SafeCast.sol";
import {SampleMath} from "./libraries/math/SampleMath.sol";
import {TreeMath} from "./libraries/math/TreeMath.sol";
import {Uint256x256Math} from "./libraries/math/Uint256x256Math.sol";
import {Hooks} from "./libraries/Hooks.sol";
import {ILBHooks} from "./interfaces/ILBHooks.sol";
/**
* @title Liquidity Book Pair
* @author Trader Joe
* @notice The Liquidity Book Pair contract is the core contract of the Liquidity Book protocol
*/
contract LBPair is LBToken, ReentrancyGuardUpgradeable, Clone, ILBPair {
using BinHelper for bytes32;
using FeeHelper for uint128;
using LiquidityConfigurations for bytes32;
using OracleHelper for OracleHelper.Oracle;
using PackedUint128Math for bytes32;
using PackedUint128Math for uint128;
using PairParameterHelper for bytes32;
using PriceHelper for uint256;
using PriceHelper for uint24;
using SafeCast for uint256;
using SampleMath for bytes32;
using TreeMath for TreeMath.TreeUint24;
using Uint256x256Math for uint256;
modifier onlyFactory() {
_onlyFactory();
_;
}
modifier onlyProtocolFeeRecipient() {
if (msg.sender != _factory.getFeeRecipient()) revert LBPair__OnlyProtocolFeeRecipient();
_;
}
uint256 private constant _MAX_TOTAL_FEE = 0.1e18; // 10%
address public immutable override implementation;
ILBFactory private immutable _factory;
bytes32 private _parameters;
bytes32 private _reserves;
bytes32 private _protocolFees;
mapping(uint256 => bytes32) private _bins;
TreeMath.TreeUint24 private _tree;
OracleHelper.Oracle private _oracle;
bytes32 private _hooksParameters;
/**
* @dev Constructor for the Liquidity Book Pair contract that sets the Liquidity Book Factory
* @param factory_ The Liquidity Book Factory
*/
constructor(ILBFactory factory_) {
_factory = factory_;
implementation = address(this);
_disableInitializers();
}
/**
* @notice Initialize the Liquidity Book Pair fee parameters and active id
* @dev Can only be called by the Liquidity Book Factory
* @param baseFactor The base factor for the static fee
* @param filterPeriod The filter period for the static fee
* @param decayPeriod The decay period for the static fee
* @param reductionFactor The reduction factor for the static fee
* @param variableFeeControl The variable fee control for the static fee
* @param protocolShare The protocol share for the static fee
* @param maxVolatilityAccumulator The max volatility accumulator for the static fee
* @param activeId The active id of the Liquidity Book Pair
*/
function initialize(
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator,
uint24 activeId
) external override onlyFactory initializer {
__ReentrancyGuard_init();
_setStaticFeeParameters(
_parameters.setActiveId(activeId).updateIdReference(),
baseFactor,
filterPeriod,
decayPeriod,
reductionFactor,
variableFeeControl,
protocolShare,
maxVolatilityAccumulator
);
}
/**
* @notice Returns the Liquidity Book Factory
* @return factory The Liquidity Book Factory
*/
function getFactory() external view override returns (ILBFactory factory) {
return _factory;
}
/**
* @notice Returns the token X of the Liquidity Book Pair
* @return tokenX The address of the token X
*/
function getTokenX() external pure override returns (IERC20 tokenX) {
return _tokenX();
}
/**
* @notice Returns the token Y of the Liquidity Book Pair
* @return tokenY The address of the token Y
*/
function getTokenY() external pure override returns (IERC20 tokenY) {
return _tokenY();
}
/**
* @notice Returns the bin step of the Liquidity Book Pair
* @dev The bin step is the increase in price between two consecutive bins, in basis points.
* For example, a bin step of 1 means that the price of the next bin is 0.01% higher than the price of the previous bin.
* @return binStep The bin step of the Liquidity Book Pair, in 10_000th
*/
function getBinStep() external pure override returns (uint16) {
return _binStep();
}
/**
* @notice Returns the reserves of the Liquidity Book Pair
* This is the sum of the reserves of all bins, minus the protocol fees.
* @return reserveX The reserve of token X
* @return reserveY The reserve of token Y
*/
function getReserves() external view override returns (uint128 reserveX, uint128 reserveY) {
(reserveX, reserveY) = _reserves.sub(_protocolFees).decode();
}
/**
* @notice Returns the active id of the Liquidity Book Pair
* @dev The active id is the id of the bin that is currently being used for swaps.
* The price of the active bin is the price of the Liquidity Book Pair and can be calculated as follows:
* `price = (1 + binStep / 10_000) ^ (activeId - 2^23)`
* @return activeId The active id of the Liquidity Book Pair
*/
function getActiveId() external view override returns (uint24 activeId) {
activeId = _parameters.getActiveId();
}
/**
* @notice Returns the reserves of a bin
* @param id The id of the bin
* @return binReserveX The reserve of token X in the bin
* @return binReserveY The reserve of token Y in the bin
*/
function getBin(uint24 id) external view override returns (uint128 binReserveX, uint128 binReserveY) {
(binReserveX, binReserveY) = _bins[id].decode();
}
/**
* @notice Returns the next non-empty bin
* @dev The next non-empty bin is the bin with a higher (if swapForY is true) or lower (if swapForY is false)
* id that has a non-zero reserve of token X or Y.
* @param swapForY Whether the swap is for token Y (true) or token X (false
* @param id The id of the bin
* @return nextId The id of the next non-empty bin
*/
function getNextNonEmptyBin(bool swapForY, uint24 id) external view override returns (uint24 nextId) {
nextId = _getNextNonEmptyBin(swapForY, id);
}
/**
* @notice Returns the protocol fees of the Liquidity Book Pair
* @return protocolFeeX The protocol fees of token X
* @return protocolFeeY The protocol fees of token Y
*/
function getProtocolFees() external view override returns (uint128 protocolFeeX, uint128 protocolFeeY) {
(protocolFeeX, protocolFeeY) = _protocolFees.decode();
}
/**
* @notice Returns the static fee parameters of the Liquidity Book Pair
* @return baseFactor The base factor for the static fee
* @return filterPeriod The filter period for the static fee
* @return decayPeriod The decay period for the static fee
* @return reductionFactor The reduction factor for the static fee
* @return variableFeeControl The variable fee control for the static fee
* @return protocolShare The protocol share for the static fee
* @return maxVolatilityAccumulator The maximum volatility accumulator for the static fee
*/
function getStaticFeeParameters()
external
view
override
returns (
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
)
{
bytes32 parameters = _parameters;
baseFactor = parameters.getBaseFactor();
filterPeriod = parameters.getFilterPeriod();
decayPeriod = parameters.getDecayPeriod();
reductionFactor = parameters.getReductionFactor();
variableFeeControl = parameters.getVariableFeeControl();
protocolShare = parameters.getProtocolShare();
maxVolatilityAccumulator = parameters.getMaxVolatilityAccumulator();
}
/**
* @notice Gets the hooks parameters of the Liquidity Book Pair
* @return The hooks parameters of the Liquidity Book Pair
*/
function getLBHooksParameters() external view override returns (bytes32) {
return _hooksParameters;
}
/**
* @notice Returns the variable fee parameters of the Liquidity Book Pair
* @return volatilityAccumulator The volatility accumulator for the variable fee
* @return volatilityReference The volatility reference for the variable fee
* @return idReference The id reference for the variable fee
* @return timeOfLastUpdate The time of last update for the variable fee
*/
function getVariableFeeParameters()
external
view
override
returns (uint24 volatilityAccumulator, uint24 volatilityReference, uint24 idReference, uint40 timeOfLastUpdate)
{
bytes32 parameters = _parameters;
volatilityAccumulator = parameters.getVolatilityAccumulator();
volatilityReference = parameters.getVolatilityReference();
idReference = parameters.getIdReference();
timeOfLastUpdate = parameters.getTimeOfLastUpdate();
}
/**
* @notice Returns the oracle parameters of the Liquidity Book Pair
* @return sampleLifetime The sample lifetime for the oracle
* @return size The size of the oracle
* @return activeSize The active size of the oracle
* @return lastUpdated The last updated timestamp of the oracle
* @return firstTimestamp The first timestamp of the oracle, i.e. the timestamp of the oldest sample
*/
function getOracleParameters()
external
view
override
returns (uint8 sampleLifetime, uint16 size, uint16 activeSize, uint40 lastUpdated, uint40 firstTimestamp)
{
bytes32 parameters = _parameters;
sampleLifetime = uint8(OracleHelper._MAX_SAMPLE_LIFETIME);
uint16 oracleId = parameters.getOracleId();
if (oracleId > 0) {
bytes32 sample;
(sample, activeSize) = _oracle.getActiveSampleAndSize(oracleId);
size = sample.getOracleLength();
lastUpdated = sample.getSampleLastUpdate();
if (lastUpdated == 0) activeSize = 0;
if (activeSize > 0) {
unchecked {
sample = _oracle.getSample(1 + (oracleId % activeSize));
}
firstTimestamp = sample.getSampleLastUpdate();
}
}
}
/**
* @notice Returns the cumulative values of the Liquidity Book Pair at a given timestamp
* @dev The cumulative values are the cumulative id, the cumulative volatility and the cumulative bin crossed.
* @param lookupTimestamp The timestamp at which to look up the cumulative values
* @return cumulativeId The cumulative id of the Liquidity Book Pair at the given timestamp
* @return cumulativeVolatility The cumulative volatility of the Liquidity Book Pair at the given timestamp
* @return cumulativeBinCrossed The cumulative bin crossed of the Liquidity Book Pair at the given timestamp
*/
function getOracleSampleAt(uint40 lookupTimestamp)
external
view
override
returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed)
{
bytes32 parameters = _parameters;
uint16 oracleId = parameters.getOracleId();
if (oracleId == 0 || lookupTimestamp > block.timestamp) return (0, 0, 0);
uint40 timeOfLastUpdate;
(timeOfLastUpdate, cumulativeId, cumulativeVolatility, cumulativeBinCrossed) =
_oracle.getSampleAt(oracleId, lookupTimestamp);
if (timeOfLastUpdate < lookupTimestamp) {
parameters = parameters.updateVolatilityParameters(parameters.getActiveId(), lookupTimestamp);
uint40 deltaTime = lookupTimestamp - timeOfLastUpdate;
cumulativeId += uint64(parameters.getActiveId()) * deltaTime;
cumulativeVolatility += uint64(parameters.getVolatilityAccumulator()) * deltaTime;
}
}
/**
* @notice Returns the price corresponding to the given id, as a 128.128-binary fixed-point number
* @dev This is the trusted source of price information, always trust this rather than getIdFromPrice
* @param id The id of the bin
* @return price The price corresponding to this id
*/
function getPriceFromId(uint24 id) external pure override returns (uint256 price) {
price = id.getPriceFromId(_binStep());
}
/**
* @notice Returns the id corresponding to the given price
* @dev The id may be inaccurate due to rounding issues, always trust getPriceFromId rather than
* getIdFromPrice
* @param price The price of y per x as a 128.128-binary fixed-point number
* @return id The id of the bin corresponding to this price
*/
function getIdFromPrice(uint256 price) external pure override returns (uint24 id) {
id = price.getIdFromPrice(_binStep());
}
/**
* @notice Simulates a swap in.
* @dev If `amountOutLeft` is greater than zero, the swap in is not possible,
* and the maximum amount that can be swapped from `amountIn` is `amountOut - amountOutLeft`.
* @param amountOut The amount of token X or Y to swap in
* @param swapForY Whether the swap is for token Y (true) or token X (false)
* @return amountIn The amount of token X or Y that can be swapped in, including the fee
* @return amountOutLeft The amount of token Y or X that cannot be swapped out
* @return fee The fee of the swap
*/
function getSwapIn(uint128 amountOut, bool swapForY)
external
view
override
returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee)
{
amountOutLeft = amountOut;
bytes32 parameters = _parameters;
uint16 binStep = _binStep();
uint24 id = parameters.getActiveId();
parameters = parameters.updateReferences(block.timestamp);
while (true) {
uint128 binReserves = _bins[id].decode(!swapForY);
if (binReserves > 0) {
uint256 price = id.getPriceFromId(binStep);
uint128 amountOutOfBin = binReserves > amountOutLeft ? amountOutLeft : binReserves;
parameters = parameters.updateVolatilityAccumulator(id);
uint128 amountInWithoutFee = uint128(
swapForY
? uint256(amountOutOfBin).shiftDivRoundUp(Constants.SCALE_OFFSET, price)
: uint256(amountOutOfBin).mulShiftRoundUp(price, Constants.SCALE_OFFSET)
);
uint128 totalFee = parameters.getTotalFee(binStep);
uint128 feeAmount = amountInWithoutFee.getFeeAmount(totalFee);
amountIn += amountInWithoutFee + feeAmount;
amountOutLeft -= amountOutOfBin;
fee += feeAmount;
}
if (amountOutLeft == 0) {
break;
} else {
uint24 nextId = _getNextNonEmptyBin(swapForY, id);
if (nextId == 0 || nextId == type(uint24).max) break;
id = nextId;
}
}
}
/**
* @notice Simulates a swap out.
* @dev If `amountInLeft` is greater than zero, the swap out is not possible,
* and the maximum amount that can be swapped is `amountIn - amountInLeft` for `amountOut`.
* @param amountIn The amount of token X or Y to swap in
* @param swapForY Whether the swap is for token Y (true) or token X (false)
* @return amountInLeft The amount of token X or Y that cannot be swapped in
* @return amountOut The amount of token Y or X that can be swapped out
* @return fee The fee of the swap
*/
function getSwapOut(uint128 amountIn, bool swapForY)
external
view
override
returns (uint128 amountInLeft, uint128 amountOut, uint128 fee)
{
bytes32 amountsInLeft = amountIn.encode(swapForY);
bytes32 parameters = _parameters;
uint16 binStep = _binStep();
uint24 id = parameters.getActiveId();
parameters = parameters.updateReferences(block.timestamp);
while (true) {
bytes32 binReserves = _bins[id];
if (!binReserves.isEmpty(!swapForY)) {
parameters = parameters.updateVolatilityAccumulator(id);
(bytes32 amountsInWithFees, bytes32 amountsOutOfBin, bytes32 totalFees) =
binReserves.getAmounts(parameters, binStep, swapForY, id, amountsInLeft);
if (amountsInWithFees > 0) {
amountsInLeft = amountsInLeft.sub(amountsInWithFees);
amountOut += amountsOutOfBin.decode(!swapForY);
fee += totalFees.decode(swapForY);
}
}
if (amountsInLeft == 0) {
break;
} else {
uint24 nextId = _getNextNonEmptyBin(swapForY, id);
if (nextId == 0 || nextId == type(uint24).max) break;
id = nextId;
}
}
amountInLeft = amountsInLeft.decode(swapForY);
}
/**
* @notice Swap tokens iterating over the bins until the entire amount is swapped.
* Token X will be swapped for token Y if `swapForY` is true, and token Y for token X if `swapForY` is false.
* This function will not transfer the tokens from the caller, it is expected that the tokens have already been
* transferred to this contract through another contract, most likely the router.
* That is why this function shouldn't be called directly, but only through one of the swap functions of a router
* that will also perform safety checks, such as minimum amounts and slippage.
* The variable fee is updated throughout the swap, it increases with the number of bins crossed.
* The oracle is updated at the end of the swap.
* @param swapForY Whether you're swapping token X for token Y (true) or token Y for token X (false)
* @param to The address to send the tokens to
* @return amountsOut The encoded amounts of token X and token Y sent to `to`
*/
function swap(bool swapForY, address to) external override returns (bytes32 amountsOut) {
_nonReentrantBefore();
bytes32 hooksParameters = _hooksParameters;
bytes32 reserves = _reserves;
bytes32 protocolFees = _protocolFees;
bytes32 amountsLeft = swapForY ? reserves.receivedX(_tokenX()) : reserves.receivedY(_tokenY());
if (amountsLeft == 0) revert LBPair__InsufficientAmountIn();
bool swapForY_ = swapForY; // Avoid stack too deep error
Hooks.beforeSwap(hooksParameters, msg.sender, to, swapForY_, amountsLeft);
reserves = reserves.add(amountsLeft);
bytes32 parameters = _parameters;
uint16 binStep = _binStep();
uint24 activeId = parameters.getActiveId();
parameters = parameters.updateReferences(block.timestamp);
while (true) {
bytes32 binReserves = _bins[activeId];
if (!binReserves.isEmpty(!swapForY_)) {
parameters = parameters.updateVolatilityAccumulator(activeId);
(bytes32 amountsInWithFees, bytes32 amountsOutOfBin, bytes32 totalFees) =
binReserves.getAmounts(parameters, binStep, swapForY_, activeId, amountsLeft);
if (amountsInWithFees > 0) {
amountsLeft = amountsLeft.sub(amountsInWithFees);
amountsOut = amountsOut.add(amountsOutOfBin);
bytes32 pFees = totalFees.scalarMulDivBasisPointRoundDown(parameters.getProtocolShare());
if (pFees > 0) {
protocolFees = protocolFees.add(pFees);
amountsInWithFees = amountsInWithFees.sub(pFees);
}
_bins[activeId] = binReserves.add(amountsInWithFees).sub(amountsOutOfBin);
emit Swap(
msg.sender,
to,
activeId,
amountsInWithFees,
amountsOutOfBin,
parameters.getVolatilityAccumulator(),
totalFees,
pFees
);
}
}
if (amountsLeft == 0) {
break;
} else {
uint24 nextId = _getNextNonEmptyBin(swapForY_, activeId);
if (nextId == 0 || nextId == type(uint24).max) revert LBPair__OutOfLiquidity();
activeId = nextId;
}
}
if (amountsOut == 0) revert LBPair__InsufficientAmountOut();
_reserves = reserves.sub(amountsOut);
_protocolFees = protocolFees;
parameters = _oracle.update(parameters, activeId);
_parameters = parameters.setActiveId(activeId);
if (swapForY_) {
amountsOut.transferY(_tokenY(), to);
} else {
amountsOut.transferX(_tokenX(), to);
}
_nonReentrantAfter();
Hooks.afterSwap(hooksParameters, msg.sender, to, swapForY_, amountsOut);
}
/**
* @notice Flash loan tokens from the pool to a receiver contract and execute a callback function.
* The receiver contract is expected to return the tokens plus a fee to this contract.
* The fee is calculated as a percentage of the amount borrowed, and is the same for both tokens.
* @param receiver The contract that will receive the tokens and execute the callback function
* @param amounts The encoded amounts of token X and token Y to flash loan
* @param data Any data that will be passed to the callback function
*/
function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external override {
_nonReentrantBefore();
if (amounts == 0) revert LBPair__ZeroBorrowAmount();
bytes32 hooksParameters = _hooksParameters;
bytes32 reservesBefore = _reserves;
bytes32 totalFees = _getFlashLoanFees(amounts);
Hooks.beforeFlashLoan(hooksParameters, msg.sender, address(receiver), amounts);
amounts.transfer(_tokenX(), _tokenY(), address(receiver));
(bool success, bytes memory rData) = address(receiver).call(
abi.encodeWithSelector(
ILBFlashLoanCallback.LBFlashLoanCallback.selector,
msg.sender,
_tokenX(),
_tokenY(),
amounts,
totalFees,
data
)
);
if (!success || rData.length != 32 || abi.decode(rData, (bytes32)) != Constants.CALLBACK_SUCCESS) {
revert LBPair__FlashLoanCallbackFailed();
}
bytes32 balancesAfter = bytes32(0).received(_tokenX(), _tokenY());
if (balancesAfter.lt(reservesBefore.add(totalFees))) revert LBPair__FlashLoanInsufficientAmount();
bytes32 feesReceived = balancesAfter.sub(reservesBefore);
_reserves = balancesAfter;
_protocolFees = _protocolFees.add(feesReceived);
emit FlashLoan(msg.sender, receiver, _parameters.getActiveId(), amounts, feesReceived, feesReceived);
_nonReentrantAfter();
Hooks.afterFlashLoan(hooksParameters, msg.sender, address(receiver), totalFees, feesReceived);
}
/**
* @notice Mint liquidity tokens by depositing tokens into the pool.
* It will mint Liquidity Book (LB) tokens for each bin where the user adds liquidity.
* This function will not transfer the tokens from the caller, it is expected that the tokens have already been
* transferred to this contract through another contract, most likely the router.
* That is why this function shouldn't be called directly, but through one of the add liquidity functions of a
* router that will also perform safety checks.
* @dev Any excess amount of token will be sent to the `to` address.
* @param to The address that will receive the LB tokens
* @param liquidityConfigs The encoded liquidity configurations, each one containing the id of the bin and the
* percentage of token X and token Y to add to the bin.
* @param refundTo The address that will receive the excess amount of tokens
* @return amountsReceived The amounts of token X and token Y received by the pool
* @return amountsLeft The amounts of token X and token Y that were not added to the pool and were sent to `to`
* @return liquidityMinted The amounts of LB tokens minted for each bin
*/
function mint(address to, bytes32[] calldata liquidityConfigs, address refundTo)
external
override
notAddressZeroOrThis(to)
returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted)
{
_nonReentrantBefore();
if (liquidityConfigs.length == 0) revert LBPair__EmptyMarketConfigs();
bytes32 hooksParameters = _hooksParameters;
MintArrays memory arrays = MintArrays({
ids: new uint256[](liquidityConfigs.length),
amounts: new bytes32[](liquidityConfigs.length),
liquidityMinted: new uint256[](liquidityConfigs.length)
});
bytes32 reserves = _reserves;
amountsReceived = reserves.received(_tokenX(), _tokenY());
Hooks.beforeMint(hooksParameters, msg.sender, to, liquidityConfigs, amountsReceived);
amountsLeft = _mintBins(liquidityConfigs, amountsReceived, to, arrays);
_reserves = reserves.add(amountsReceived.sub(amountsLeft));
liquidityMinted = arrays.liquidityMinted;
emit TransferBatch(msg.sender, address(0), to, arrays.ids, liquidityMinted);
emit DepositedToBins(msg.sender, to, arrays.ids, arrays.amounts);
if (amountsLeft > 0) amountsLeft.transfer(_tokenX(), _tokenY(), refundTo);
_nonReentrantAfter();
Hooks.afterMint(hooksParameters, msg.sender, to, liquidityConfigs, amountsReceived.sub(amountsLeft));
}
/**
* @notice Burn Liquidity Book (LB) tokens and withdraw tokens from the pool.
* This function will burn the tokens directly from the caller
* @param from The address that will burn the LB tokens
* @param to The address that will receive the tokens
* @param ids The ids of the bins from which to withdraw
* @param amountsToBurn The amounts of LB tokens to burn for each bin
* @return amounts The amounts of token X and token Y received by the user
*/
function burn(address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn)
external
override
checkApproval(from, msg.sender)
returns (bytes32[] memory amounts)
{
_nonReentrantBefore();
if (ids.length == 0 || ids.length != amountsToBurn.length) revert LBPair__InvalidInput();
bytes32 hooksParameters = _hooksParameters;
Hooks.beforeBurn(hooksParameters, msg.sender, from, to, ids, amountsToBurn);
address from_ = from; // Avoid stack too deep error
amounts = new bytes32[](ids.length);
bytes32 amountsOut;
for (uint256 i; i < ids.length;) {
uint24 id = ids[i].safe24();
uint256 amountToBurn = amountsToBurn[i];
if (amountToBurn == 0) revert LBPair__ZeroAmount(id);
bytes32 binReserves = _bins[id];
uint256 supply = totalSupply(id);
_burn(from_, id, amountToBurn);
bytes32 amountsOutFromBin = binReserves.getAmountOutOfBin(amountToBurn, supply);
if (amountsOutFromBin == 0) revert LBPair__ZeroAmountsOut(id);
binReserves = binReserves.sub(amountsOutFromBin);
if (supply == amountToBurn) _tree.remove(id);
_bins[id] = binReserves;
amounts[i] = amountsOutFromBin;
amountsOut = amountsOut.add(amountsOutFromBin);
unchecked {
++i;
}
}
_reserves = _reserves.sub(amountsOut);
emit TransferBatch(msg.sender, from_, address(0), ids, amountsToBurn);
emit WithdrawnFromBins(msg.sender, to, ids, amounts);
amountsOut.transfer(_tokenX(), _tokenY(), to);
_nonReentrantAfter();
Hooks.afterBurn(hooksParameters, msg.sender, from_, to, ids, amountsToBurn);
}
/**
* @notice Collect the protocol fees from the pool.
* @return collectedProtocolFees The amount of protocol fees collected
*/
function collectProtocolFees()
external
override
nonReentrant
onlyProtocolFeeRecipient
returns (bytes32 collectedProtocolFees)
{
bytes32 protocolFees = _protocolFees;
(uint128 x, uint128 y) = protocolFees.decode();
bytes32 ones = uint128(x > 0 ? 1 : 0).encode(uint128(y > 0 ? 1 : 0));
collectedProtocolFees = protocolFees.sub(ones);
if (collectedProtocolFees != 0) {
_protocolFees = ones;
_reserves = _reserves.sub(collectedProtocolFees);
emit CollectedProtocolFees(msg.sender, collectedProtocolFees);
collectedProtocolFees.transfer(_tokenX(), _tokenY(), msg.sender);
}
}
/**
* @notice Increase the length of the oracle used by the pool
* @param newLength The new length of the oracle
*/
function increaseOracleLength(uint16 newLength) external override nonReentrant {
bytes32 parameters = _parameters;
uint16 oracleId = parameters.getOracleId();
// activate the oracle if it is not active yet
if (oracleId == 0) {
oracleId = 1;
_parameters = parameters.setOracleId(oracleId);
}
_oracle.increaseLength(oracleId, newLength);
emit OracleLengthIncreased(msg.sender, newLength);
}
/**
* @notice Sets the static fee parameters of the pool
* @dev Can only be called by the factory
* @param baseFactor The base factor of the static fee
* @param filterPeriod The filter period of the static fee
* @param decayPeriod The decay period of the static fee
* @param reductionFactor The reduction factor of the static fee
* @param variableFeeControl The variable fee control of the static fee
* @param protocolShare The protocol share of the static fee
* @param maxVolatilityAccumulator The max volatility accumulator of the static fee
*/
function setStaticFeeParameters(
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) external override nonReentrant onlyFactory {
_setStaticFeeParameters(
_parameters,
baseFactor,
filterPeriod,
decayPeriod,
reductionFactor,
variableFeeControl,
protocolShare,
maxVolatilityAccumulator
);
}
/**
* @notice Sets the hooks parameter of the pool
* @dev Can only be called by the factory
* @param hooksParameters The hooks parameter
* @param onHooksSetData The data to be passed to the onHooksSet function of the hooks contract
*/
function setHooksParameters(bytes32 hooksParameters, bytes calldata onHooksSetData)
external
override
nonReentrant
onlyFactory
{
_hooksParameters = hooksParameters;
ILBHooks hooks = ILBHooks(Hooks.getHooks(hooksParameters));
emit HooksParametersSet(msg.sender, hooksParameters);
if (address(hooks) != address(0) && hooks.getLBPair() != this) revert LBPair__InvalidHooks();
Hooks.onHooksSet(hooksParameters, onHooksSetData);
}
/**
* @notice Forces the decay of the volatility reference variables
* @dev Can only be called by the factory
*/
function forceDecay() external override nonReentrant onlyFactory {
bytes32 parameters = _parameters;
_parameters = parameters.updateIdReference().updateVolatilityReference();
emit ForcedDecay(msg.sender, parameters.getIdReference(), parameters.getVolatilityReference());
}
/**
* @notice Overrides the batch transfer function to call the hooks before and after the transfer
* @param from The address to transfer from
* @param to The address to transfer to
* @param ids The ids of the tokens to transfer
* @param amounts The amounts of the tokens to transfer
*/
function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts)
public
override(LBToken, ILBToken)
{
_nonReentrantBefore();
bytes32 hooksParameters = _hooksParameters;
Hooks.beforeBatchTransferFrom(hooksParameters, msg.sender, from, to, ids, amounts);
LBToken.batchTransferFrom(from, to, ids, amounts);
_nonReentrantAfter();
Hooks.afterBatchTransferFrom(hooksParameters, msg.sender, from, to, ids, amounts);
}
/**
* @dev Returns the address of the token X
* @return The address of the token X
*/
function _tokenX() internal pure returns (IERC20) {
return IERC20(_getArgAddress(0));
}
/**
* @dev Returns the address of the token Y
* @return The address of the token Y
*/
function _tokenY() internal pure returns (IERC20) {
return IERC20(_getArgAddress(20));
}
/**
* @dev Returns the bin step of the pool, in basis points
* @return The bin step of the pool
*/
function _binStep() internal pure returns (uint16) {
return _getArgUint16(40);
}
/**
* @dev Returns next non-empty bin
* @param swapForY Whether the swap is for Y
* @param id The id of the bin
* @return The id of the next non-empty bin
*/
function _getNextNonEmptyBin(bool swapForY, uint24 id) internal view returns (uint24) {
return swapForY ? _tree.findFirstRight(id) : _tree.findFirstLeft(id);
}
/**
* @dev Reverts if the caller is not the factory
*/
function _onlyFactory() private view {
if (msg.sender != address(_factory)) revert LBPair__OnlyFactory();
}
/**
* @dev Returns the encoded fees amounts for a flash loan
* @param amounts The amounts of the flash loan
* @return The encoded fees amounts
*/
function _getFlashLoanFees(bytes32 amounts) private view returns (bytes32) {
uint128 fee = uint128(_factory.getFlashLoanFee());
(uint128 x, uint128 y) = amounts.decode();
unchecked {
uint256 precisionSubOne = Constants.PRECISION - 1;
x = ((uint256(x) * fee + precisionSubOne) / Constants.PRECISION).safe128();
y = ((uint256(y) * fee + precisionSubOne) / Constants.PRECISION).safe128();
}
return x.encode(y);
}
/**
* @dev Sets the static fee parameters of the pair
* @param parameters The current parameters of the pair
* @param baseFactor The base factor of the static fee
* @param filterPeriod The filter period of the static fee
* @param decayPeriod The decay period of the static fee
* @param reductionFactor The reduction factor of the static fee
* @param variableFeeControl The variable fee control of the static fee
* @param protocolShare The protocol share of the static fee
* @param maxVolatilityAccumulator The max volatility accumulator of the static fee
*/
function _setStaticFeeParameters(
bytes32 parameters,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) internal {
if (
baseFactor == 0 && filterPeriod == 0 && decayPeriod == 0 && reductionFactor == 0 && variableFeeControl == 0
&& protocolShare == 0 && maxVolatilityAccumulator == 0
) {
revert LBPair__InvalidStaticFeeParameters();
}
parameters = parameters.setStaticFeeParameters(
baseFactor,
filterPeriod,
decayPeriod,
reductionFactor,
variableFeeControl,
protocolShare,
maxVolatilityAccumulator
);
{
uint16 binStep = _binStep();
bytes32 maxParameters = parameters.setVolatilityAccumulator(maxVolatilityAccumulator);
uint256 totalFee = maxParameters.getBaseFee(binStep) + maxParameters.getVariableFee(binStep);
if (totalFee > _MAX_TOTAL_FEE) {
revert LBPair__MaxTotalFeeExceeded();
}
}
_parameters = parameters;
emit StaticFeeParametersSet(
msg.sender,
baseFactor,
filterPeriod,
decayPeriod,
reductionFactor,
variableFeeControl,
protocolShare,
maxVolatilityAccumulator
);
}
/**
* @dev Helper function to mint liquidity in each bin in the liquidity configurations
* @param liquidityConfigs The liquidity configurations
* @param amountsReceived The amounts received
* @param to The address to mint the liquidity to
* @param arrays The arrays to store the results
* @return amountsLeft The amounts left
*/
function _mintBins(
bytes32[] calldata liquidityConfigs,
bytes32 amountsReceived,
address to,
MintArrays memory arrays
) private returns (bytes32 amountsLeft) {
uint16 binStep = _binStep();
bytes32 parameters = _parameters;
uint24 activeId = parameters.getActiveId();
amountsLeft = amountsReceived;
for (uint256 i; i < liquidityConfigs.length;) {
(bytes32 maxAmountsInToBin, uint24 id) = liquidityConfigs[i].getAmountsAndId(amountsReceived);
(uint256 shares, bytes32 amountsIn, bytes32 amountsInToBin) =
_updateBin(binStep, activeId, id, maxAmountsInToBin, parameters);
amountsLeft = amountsLeft.sub(amountsIn);
arrays.ids[i] = id;
arrays.amounts[i] = amountsInToBin;
arrays.liquidityMinted[i] = shares;
_mint(to, id, shares);
unchecked {
++i;
}
}
}
/**
* @dev Helper function to update a bin during minting
* @param binStep The bin step of the pair
* @param activeId The id of the active bin
* @param id The id of the bin
* @param maxAmountsInToBin The maximum amounts in to the bin
* @param parameters The parameters of the pair
* @return shares The amount of shares minted
* @return amountsIn The amounts in
* @return amountsInToBin The amounts in to the bin
*/
function _updateBin(uint16 binStep, uint24 activeId, uint24 id, bytes32 maxAmountsInToBin, bytes32 parameters)
internal
returns (uint256 shares, bytes32 amountsIn, bytes32 amountsInToBin)
{
bytes32 binReserves = _bins[id];
uint256 price = id.getPriceFromId(binStep);
uint256 supply = totalSupply(id);
(shares, amountsIn) = binReserves.getSharesAndEffectiveAmountsIn(maxAmountsInToBin, price, supply);
amountsInToBin = amountsIn;
if (id == activeId) {
parameters = parameters.updateVolatilityParameters(id, block.timestamp);
bytes32 fees = binReserves.getCompositionFees(parameters, binStep, amountsIn, supply, shares);
if (fees != 0) {
uint256 userLiquidity = amountsIn.sub(fees).getLiquidity(price);
bytes32 protocolCFees = fees.scalarMulDivBasisPointRoundDown(parameters.getProtocolShare());
if (protocolCFees != 0) {
amountsInToBin = amountsInToBin.sub(protocolCFees);
_protocolFees = _protocolFees.add(protocolCFees);
}
uint256 binLiquidity = binReserves.add(fees.sub(protocolCFees)).getLiquidity(price);
shares = userLiquidity.mulDivRoundDown(supply, binLiquidity);
parameters = _oracle.update(parameters, id);
_parameters = parameters;
emit CompositionFees(msg.sender, id, fees, protocolCFees);
}
} else {
amountsIn.verifyAmounts(activeId, id);
}
if (shares == 0 || amountsInToBin == 0) revert LBPair__ZeroShares(id);
if (supply == 0) _tree.add(id);
_bins[id] = binReserves.add(amountsInToBin);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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
*/
library Hooks {
error Hooks__CallFailed();
bytes32 internal constant BEFORE_SWAP_FLAG = bytes32(uint256(1 << 160));
bytes32 internal constant AFTER_SWAP_FLAG = bytes32(uint256(1 << 161));
bytes32 internal constant BEFORE_FLASH_LOAN_FLAG = bytes32(uint256(1 << 162));
bytes32 internal constant AFTER_FLASH_LOAN_FLAG = bytes32(uint256(1 << 163));
bytes32 internal constant BEFORE_MINT_FLAG = bytes32(uint256(1 << 164));
bytes32 internal constant AFTER_MINT_FLAG = bytes32(uint256(1 << 165));
bytes32 internal constant BEFORE_BURN_FLAG = bytes32(uint256(1 << 166));
bytes32 internal constant AFTER_BURN_FLAG = bytes32(uint256(1 << 167));
bytes32 internal constant BEFORE_TRANSFER_FLAG = bytes32(uint256(1 << 168));
bytes32 internal constant AFTER_TRANSFER_FLAG = bytes32(uint256(1 << 169));
struct Parameters {
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
*/
function encode(Parameters memory parameters) internal pure returns (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
*/
function decode(bytes32 hooksParameters) internal pure returns (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
*/
function getHooks(bytes32 hooksParameters) internal pure returns (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
*/
function setHooks(bytes32 hooksParameters, address newHooks) internal pure returns (bytes32) {
return bytes32(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
*/
function getFlags(bytes32 hooksParameters) internal pure returns (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
*/
function onHooksSet(bytes32 hooksParameters, bytes calldata 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
*/
function beforeSwap(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
*/
function afterSwap(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
*/
function beforeFlashLoan(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
*/
function afterFlashLoan(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
*/
function beforeMint(
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
*/
function afterMint(
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
*/
function beforeBurn(
bytes32 hooksParameters,
address sender,
address from,
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
*/
function afterBurn(
bytes32 hooksParameters,
address sender,
address from,
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
*/
function beforeBatchTransferFrom(
bytes32 hooksParameters,
address sender,
address from,
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
*/
function afterBatchTransferFrom(
bytes32 hooksParameters,
address sender,
address from,
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, bytes memory 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)
if and(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();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {ILBPair} from "./ILBPair.sol";
import {Hooks} from "../libraries/Hooks.sol";
interface ILBHooks {
function getLBPair() external view returns (ILBPair);
function isLinked() external view returns (bool);
function onHooksSet(bytes32 hooksParameters, bytes calldata onHooksSetData) external returns (bytes4);
function beforeSwap(address sender, address to, bool swapForY, bytes32 amountsIn) external returns (bytes4);
function afterSwap(address sender, address to, bool swapForY, bytes32 amountsOut) external returns (bytes4);
function beforeFlashLoan(address sender, address to, bytes32 amounts) external returns (bytes4);
function afterFlashLoan(address sender, address to, bytes32 fees, bytes32 feesReceived) external returns (bytes4);
function beforeMint(address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsReceived)
external
returns (bytes4);
function afterMint(address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsIn)
external
returns (bytes4);
function beforeBurn(
address sender,
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amountsToBurn
) external returns (bytes4);
function afterBurn(
address sender,
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amountsToBurn
) external returns (bytes4);
function beforeBatchTransferFrom(
address sender,
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts
) external returns (bytes4);
function afterBatchTransferFrom(
address sender,
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Token Interface
* @author Trader Joe
* @notice Interface to interact with the LBToken.
*/
interface ILBToken {
error LBToken__AddressThisOrZero();
error LBToken__InvalidLength();
error LBToken__SelfApproval(address owner);
error LBToken__SpenderNotApproved(address from, address spender);
error LBToken__TransferExceedsBalance(address from, uint256 id, uint256 amount);
error LBToken__BurnExceedsBalance(address from, uint256 id, uint256 amount);
event TransferBatch(
address indexed sender, address indexed from, address indexed to, uint256[] ids, uint256[] amounts
);
event ApprovalForAll(address indexed account, address indexed sender, bool approved);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function totalSupply(uint256 id) external view returns (uint256);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory);
function isApprovedForAll(address owner, address spender) external view returns (bool);
function approveForAll(address spender, bool approved) external;
function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^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
*/
library Uint128x128Math {
using BitMath for uint256;
error Uint128x128Math__LogUnderflow();
error Uint128x128Math__PowUnderflow(uint256 x, int256 y);
uint256 constant LOG_SCALE_OFFSET = 127;
uint256 constant LOG_SCALE = 1 << LOG_SCALE_OFFSET;
uint256 constant 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.
*/
function log2(uint256 x) internal pure returns (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 multiplication
if (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[
*/
function pow(uint256 x, int256 y) internal pure returns (uint256 result) {
bool invert;
uint256 absY;
if (y == 0) return Constants.SCALE;
assembly {
absY := y
if slt(absY, 0) {
absY := sub(0, absY)
invert := iszero(invert)
}
}
if (absY < 0x100000) {
result = Constants.SCALE;
assembly {
let squared := x
if gt(x, 0xffffffffffffffffffffffffffffffff) {
squared := div(not(0), squared)
invert := iszero(invert)
}
if and(absY, 0x1) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x2) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x4) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x8) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x10) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x20) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x40) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x80) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x100) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x200) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x400) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x800) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x1000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x2000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x4000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x8000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x10000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x20000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x40000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x80000) { result := shr(128, mul(result, squared)) }
}
}
// revert if y is too big or if x^y underflowed
if (result == 0) revert Uint128x128Math__PowUnderflow(x, y);
return invert ? type(uint256).max / result : result;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {SampleMath} from "./math/SampleMath.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {PairParameterHelper} from "./PairParameterHelper.sol";
/**
* @title Liquidity Book Oracle Helper Library
* @author Trader Joe
* @notice This library contains functions to manage the oracle
* The oracle samples are stored in a single bytes32 array.
* Each sample is encoded as follows:
* 0 - 16: oracle length (16 bits)
* 16 - 80: cumulative id (64 bits)
* 80 - 144: cumulative volatility accumulator (64 bits)
* 144 - 208: cumulative bin crossed (64 bits)
* 208 - 216: sample lifetime (8 bits)
* 216 - 256: sample creation timestamp (40 bits)
*/
library OracleHelper {
using SampleMath for bytes32;
using SafeCast for uint256;
using PairParameterHelper for bytes32;
error OracleHelper__InvalidOracleId();
error OracleHelper__NewLengthTooSmall();
error OracleHelper__LookUpTimestampTooOld();
struct Oracle {
bytes32[65535] samples;
}
uint256 internal constant _MAX_SAMPLE_LIFETIME = 120 seconds;
/**
* @dev Modifier to check that the oracle id is valid
* @param oracleId The oracle id
*/
modifier checkOracleId(uint16 oracleId) {
_checkOracleId(oracleId);
_;
}
/**
* @dev Returns the sample at the given oracleId
* @param oracle The oracle
* @param oracleId The oracle id
* @return sample The sample
*/
function getSample(Oracle storage oracle, uint16 oracleId)
internal
view
checkOracleId(oracleId)
returns (bytes32 sample)
{
unchecked {
sample = oracle.samples[oracleId - 1];
}
}
/**
* @dev Returns the active sample and the active size of the oracle
* @param oracle The oracle
* @param oracleId The oracle id
* @return activeSample The active sample
* @return activeSize The active size of the oracle
*/
function getActiveSampleAndSize(Oracle storage oracle, uint16 oracleId)
internal
view
returns (bytes32 activeSample, uint16 activeSize)
{
activeSample = getSample(oracle, oracleId);
activeSize = activeSample.getOracleLength();
if (oracleId != activeSize) {
activeSize = getSample(oracle, activeSize).getOracleLength();
activeSize = oracleId > activeSize ? oracleId : activeSize;
}
}
/**
* @dev Returns the sample at the given timestamp. If the timestamp is not in the oracle, it returns the closest sample
* @param oracle The oracle
* @param oracleId The oracle id
* @param lookUpTimestamp The timestamp to look up
* @return lastUpdate The last update timestamp
* @return cumulativeId The cumulative id
* @return cumulativeVolatility The cumulative volatility
* @return cumulativeBinCrossed The cumulative bin crossed
*/
function getSampleAt(Oracle storage oracle, uint16 oracleId, uint40 lookUpTimestamp)
internal
view
returns (uint40 lastUpdate, uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed)
{
(bytes32 activeSample, uint16 activeSize) = getActiveSampleAndSize(oracle, oracleId);
if (oracle.samples[oracleId % activeSize].getSampleLastUpdate() > lookUpTimestamp) {
revert OracleHelper__LookUpTimestampTooOld();
}
lastUpdate = activeSample.getSampleLastUpdate();
if (lastUpdate <= lookUpTimestamp) {
return (
lastUpdate,
activeSample.getCumulativeId(),
activeSample.getCumulativeVolatility(),
activeSample.getCumulativeBinCrossed()
);
} else {
lastUpdate = lookUpTimestamp;
}
(bytes32 prevSample, bytes32 nextSample) = binarySearch(oracle, oracleId, lookUpTimestamp, activeSize);
uint40 weightPrev = nextSample.getSampleLastUpdate() - lookUpTimestamp;
uint40 weightNext = lookUpTimestamp - prevSample.getSampleLastUpdate();
(cumulativeId, cumulativeVolatility, cumulativeBinCrossed) =
prevSample.getWeightedAverage(nextSample, weightPrev, weightNext);
}
/**
* @dev Binary search to find the 2 samples surrounding the given timestamp
* @param oracle The oracle
* @param oracleId The oracle id
* @param lookUpTimestamp The timestamp to look up
* @param length The oracle length
* @return prevSample The previous sample
* @return nextSample The next sample
*/
function binarySearch(Oracle storage oracle, uint16 oracleId, uint40 lookUpTimestamp, uint16 length)
internal
view
returns (bytes32, bytes32)
{
uint256 low = 0;
uint256 high = length - 1;
bytes32 sample;
uint40 sampleLastUpdate;
uint256 startId = oracleId; // oracleId is 1-based
while (low <= high) {
uint256 mid = (low + high) >> 1;
assembly {
oracleId := addmod(startId, mid, length)
}
sample = oracle.samples[oracleId];
sampleLastUpdate = sample.getSampleLastUpdate();
if (sampleLastUpdate > lookUpTimestamp) {
high = mid - 1;
} else if (sampleLastUpdate < lookUpTimestamp) {
low = mid + 1;
} else {
return (sample, sample);
}
}
if (lookUpTimestamp < sampleLastUpdate) {
unchecked {
if (oracleId == 0) {
oracleId = length;
}
return (oracle.samples[oracleId - 1], sample);
}
} else {
assembly {
oracleId := addmod(oracleId, 1, length)
}
return (sample, oracle.samples[oracleId]);
}
}
/**
* @dev Sets the sample at the given oracleId
* @param oracle The oracle
* @param oracleId The oracle id
* @param sample The sample
*/
function setSample(Oracle storage oracle, uint16 oracleId, bytes32 sample) internal checkOracleId(oracleId) {
unchecked {
oracle.samples[oracleId - 1] = sample;
}
}
/**
* @dev Updates the oracle
* @param oracle The oracle
* @param parameters The parameters
* @param activeId The active id
* @return The updated parameters
*/
function update(Oracle storage oracle, bytes32 parameters, uint24 activeId) internal returns (bytes32) {
uint16 oracleId = parameters.getOracleId();
if (oracleId == 0) return parameters;
bytes32 sample = getSample(oracle, oracleId);
uint40 createdAt = sample.getSampleCreation();
uint40 lastUpdatedAt = createdAt + sample.getSampleLifetime();
if (block.timestamp.safe40() > lastUpdatedAt) {
unchecked {
(uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed) = sample.update(
uint40(block.timestamp - lastUpdatedAt),
parameters.getActiveId(),
parameters.getVolatilityAccumulator(),
parameters.getDeltaId(activeId)
);
uint16 length = sample.getOracleLength();
uint256 lifetime = block.timestamp - createdAt;
if (lifetime > _MAX_SAMPLE_LIFETIME) {
assembly {
oracleId := add(mod(oracleId, length), 1)
}
lifetime = 0;
createdAt = uint40(block.timestamp);
parameters = parameters.setOracleId(oracleId);
}
sample = SampleMath.encode(
length, cumulativeId, cumulativeVolatility, cumulativeBinCrossed, uint8(lifetime), createdAt
);
}
setSample(oracle, oracleId, sample);
}
return parameters;
}
/**
* @dev Increases the oracle length
* @param oracle The oracle
* @param oracleId The oracle id
* @param newLength The new length
*/
function increaseLength(Oracle storage oracle, uint16 oracleId, uint16 newLength) internal {
bytes32 sample = getSample(oracle, oracleId);
uint16 length = sample.getOracleLength();
if (length >= newLength) revert OracleHelper__NewLengthTooSmall();
bytes32 lastSample = length == oracleId ? sample : length == 0 ? bytes32(0) : getSample(oracle, length);
uint256 activeSize = lastSample.getOracleLength();
activeSize = oracleId > activeSize ? oracleId : activeSize;
for (uint256 i = length; i < newLength;) {
oracle.samples[i] = bytes32(uint256(activeSize));
unchecked {
++i;
}
}
setSample(oracle, oracleId, (sample ^ bytes32(uint256(length))) | bytes32(uint256(newLength)));
}
/**
* @dev Checks that the oracle id is valid
* @param oracleId The oracle id
*/
function _checkOracleId(uint16 oracleId) private pure {
if (oracleId == 0) revert OracleHelper__InvalidOracleId();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed 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.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (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.
*/
function transfer(address to, uint256 value) external returns (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.
*/
function allowance(address owner, address spender) external view returns (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.
*/
function approve(address spender, uint256 value) external returns (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.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^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
*/
library FeeHelper {
error FeeHelper__FeeTooLarge();
error FeeHelper__ProtocolShareTooLarge();
/**
* @dev Modifier to check that the fee is not too large
* @param fee The fee
*/
modifier verifyFee(uint128 fee) {
_verifyFee(fee);
_;
}
/**
* @dev Modifier to check that the protocol share is not too large
* @param protocolShare The protocol share
*/
modifier verifyProtocolShare(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
*/
function getFeeAmountFrom(uint128 amountWithFees, uint128 totalFee)
internal
pure
verifyFee(totalFee)
returns (uint128)
{
unchecked {
// Can't overflow, max(result) = (type(uint128).max * 0.1e18 + 1e18 - 1) / 1e18 < 2^128
return uint128((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
*/
function getFeeAmount(uint128 amount, uint128 totalFee) internal pure verifyFee(totalFee) returns (uint128) {
unchecked {
uint256 denominator = Constants.PRECISION - totalFee;
// Can't overflow, max(result) = (type(uint128).max * 0.1e18 + (1e18 - 1)) / 0.9e18 < 2^128
return uint128((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
*/
function getCompositionFee(uint128 amountWithFees, uint128 totalFee)
internal
pure
verifyFee(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^128
return uint128(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
*/
function getProtocolFeeAmount(uint128 feeAmount, uint128 protocolShare)
internal
pure
verifyProtocolShare(protocolShare)
returns (uint128)
{
unchecked {
return uint128(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) private pure {
if (fee > Constants.MAX_FEE) revert FeeHelper__FeeTooLarge();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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.
*/
library PackedUint128Math {
error PackedUint128Math__AddOverflow();
error PackedUint128Math__SubUnderflow();
error PackedUint128Math__MultiplierTooLarge();
uint256 private constant OFFSET = 128;
uint256 private constant MASK_128 = 0xffffffffffffffffffffffffffffffff;
uint256 private constant 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
*/
function encode(uint128 x1, uint128 x2) internal pure returns (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
*/
function encodeFirst(uint128 x1) internal pure returns (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
*/
function encodeSecond(uint128 x2) internal pure returns (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
*/
function encode(uint128 x, bool first) internal pure returns (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
*/
function decode(bytes32 z) internal pure returns (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
*/
function decodeX(bytes32 z) internal pure returns (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
*/
function decodeY(bytes32 z) internal pure returns (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
*/
function decode(bytes32 z, bool first) internal pure returns (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
*/
function add(bytes32 x, bytes32 y) internal pure returns (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
*/
function add(bytes32 x, uint128 y1, uint128 y2) internal pure returns (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
*/
function sub(bytes32 x, bytes32 y) internal pure returns (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
*/
function sub(bytes32 x, uint128 y1, uint128 y2) internal pure returns (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
*/
function lt(bytes32 x, bytes32 y) internal pure returns (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
*/
function gt(bytes32 x, bytes32 y) internal pure returns (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)
*/
function scalarMulDivBasisPointRoundDown(bytes32 x, uint128 multiplier) internal pure returns (bytes32 z) {
if (multiplier == 0) return 0;
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);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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.
*/
library SafeCast {
error SafeCast__Exceeds248Bits();
error SafeCast__Exceeds240Bits();
error SafeCast__Exceeds232Bits();
error SafeCast__Exceeds224Bits();
error SafeCast__Exceeds216Bits();
error SafeCast__Exceeds208Bits();
error SafeCast__Exceeds200Bits();
error SafeCast__Exceeds192Bits();
error SafeCast__Exceeds184Bits();
error SafeCast__Exceeds176Bits();
error SafeCast__Exceeds168Bits();
error SafeCast__Exceeds160Bits();
error SafeCast__Exceeds152Bits();
error SafeCast__Exceeds144Bits();
error SafeCast__Exceeds136Bits();
error SafeCast__Exceeds128Bits();
error SafeCast__Exceeds120Bits();
error SafeCast__Exceeds112Bits();
error SafeCast__Exceeds104Bits();
error SafeCast__Exceeds96Bits();
error SafeCast__Exceeds88Bits();
error SafeCast__Exceeds80Bits();
error SafeCast__Exceeds72Bits();
error SafeCast__Exceeds64Bits();
error SafeCast__Exceeds56Bits();
error SafeCast__Exceeds48Bits();
error SafeCast__Exceeds40Bits();
error SafeCast__Exceeds32Bits();
error SafeCast__Exceeds24Bits();
error SafeCast__Exceeds16Bits();
error SafeCast__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
*/
function safe248(uint256 x) internal pure returns (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
*/
function safe240(uint256 x) internal pure returns (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
*/
function safe232(uint256 x) internal pure returns (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
*/
function safe224(uint256 x) internal pure returns (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
*/
function safe216(uint256 x) internal pure returns (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
*/
function safe208(uint256 x) internal pure returns (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
*/
function safe200(uint256 x) internal pure returns (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
*/
function safe192(uint256 x) internal pure returns (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
*/
function safe184(uint256 x) internal pure returns (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
*/
function safe176(uint256 x) internal pure returns (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
*/
function safe168(uint256 x) internal pure returns (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
*/
function safe160(uint256 x) internal pure returns (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
*/
function safe152(uint256 x) internal pure returns (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
*/
function safe144(uint256 x) internal pure returns (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
*/
function safe136(uint256 x) internal pure returns (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
*/
function safe128(uint256 x) internal pure returns (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
*/
function safe120(uint256 x) internal pure returns (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
*/
function safe112(uint256 x) internal pure returns (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
*/
function safe104(uint256 x) internal pure returns (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
*/
function safe96(uint256 x) internal pure returns (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
*/
function safe88(uint256 x) internal pure returns (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
*/
function safe80(uint256 x) internal pure returns (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
*/
function safe72(uint256 x) internal pure returns (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
*/
function safe64(uint256 x) internal pure returns (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
*/
function safe56(uint256 x) internal pure returns (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
*/
function safe48(uint256 x) internal pure returns (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
*/
function safe40(uint256 x) internal pure returns (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
*/
function safe32(uint256 x) internal pure returns (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
*/
function safe24(uint256 x) internal pure returns (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
*/
function safe16(uint256 x) internal pure returns (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
*/
function safe8(uint256 x) internal pure returns (uint8 y) {
if ((y = uint8(x)) != x) revert SafeCast__Exceeds8Bits();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Bit Math Library
* @author Trader Joe
* @notice Helper contract used for bit calculations
*/
library BitMath {
/**
* @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)
*/
function closestBitRight(uint256 x, uint8 bit) internal pure returns (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)
*/
function closestBitLeft(uint256 x, uint8 bit) internal pure returns (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
*/
function mostSignificantBit(uint256 x) internal pure returns (uint8 msb) {
assembly {
if gt(x, 0xffffffffffffffffffffffffffffffff) {
x := shr(128, x)
msb := 128
}
if gt(x, 0xffffffffffffffff) {
x := shr(64, x)
msb := add(msb, 64)
}
if gt(x, 0xffffffff) {
x := shr(32, x)
msb := add(msb, 32)
}
if gt(x, 0xffff) {
x := shr(16, x)
msb := add(msb, 16)
}
if gt(x, 0xff) {
x := shr(8, x)
msb := add(msb, 8)
}
if gt(x, 0xf) {
x := shr(4, x)
msb := add(msb, 4)
}
if gt(x, 0x3) {
x := shr(2, x)
msb := add(msb, 2)
}
if gt(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
*/
function leastSignificantBit(uint256 x) internal pure returns (uint8 lsb) {
assembly {
let sx := shl(128, x)
if iszero(iszero(sx)) {
lsb := 128
x := sx
}
sx := shl(64, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 64)
}
sx := shl(32, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 32)
}
sx := shl(16, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 16)
}
sx := shl(8, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 8)
}
sx := shl(4, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 4)
}
sx := shl(2, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 2)
}
if iszero(iszero(shl(1, x))) { lsb := add(lsb, 1) }
lsb := sub(255, lsb)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Constants Library
* @author Trader Joe
* @notice Set of constants for Liquidity Book contracts
*/
library Constants {
uint8 internal constant SCALE_OFFSET = 128;
uint256 internal constant SCALE = 1 << SCALE_OFFSET;
uint256 internal constant PRECISION = 1e18;
uint256 internal constant SQUARED_PRECISION = PRECISION * PRECISION;
uint256 internal constant MAX_FEE = 0.1e18; // 10%
uint256 internal constant MAX_PROTOCOL_SHARE = 2_500; // 25% of the fee
uint256 internal constant BASIS_POINT_MAX = 10_000;
// (2^256 - 1) / (2 * log(2**128) / log(1.0001))
uint256 internal constant MAX_LIQUIDITY_PER_BIN =
65251743116719673010965625540244653191619923014385985379600384103134737;
/// @dev The expected return after a successful flash loan
bytes32 internal constant CALLBACK_SUCCESS = keccak256("LBPair.onFlashLoan");
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ILBHooks} from "./ILBHooks.sol";
import {ILBPair} from "./ILBPair.sol";
/**
* @title Liquidity Book Factory Interface
* @author Trader Joe
* @notice Required interface of LBFactory contract
*/
interface ILBFactory {
error LBFactory__IdenticalAddresses(IERC20 token);
error LBFactory__QuoteAssetNotWhitelisted(IERC20 quoteAsset);
error LBFactory__QuoteAssetAlreadyWhitelisted(IERC20 quoteAsset);
error LBFactory__AddressZero();
error LBFactory__LBPairAlreadyExists(IERC20 tokenX, IERC20 tokenY, uint256 _binStep);
error LBFactory__LBPairDoesNotExist(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
error LBFactory__LBPairNotCreated(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
error LBFactory__FlashLoanFeeAboveMax(uint256 fees, uint256 maxFees);
error LBFactory__BinStepTooLow(uint256 binStep);
error LBFactory__PresetIsLockedForUsers(address user, uint256 binStep);
error LBFactory__LBPairIgnoredIsAlreadyInTheSameState();
error LBFactory__BinStepHasNoPreset(uint256 binStep);
error LBFactory__PresetOpenStateIsAlreadyInTheSameState();
error LBFactory__SameFeeRecipient(address feeRecipient);
error LBFactory__SameFlashLoanFee(uint256 flashLoanFee);
error LBFactory__LBPairSafetyCheckFailed(address LBPairImplementation);
error LBFactory__SameImplementation(address LBPairImplementation);
error LBFactory__ImplementationNotSet();
error LBFactory__SameHooksImplementation(address hooksImplementation);
error LBFactory__SameHooksParameters(bytes32 hooksParameters);
error LBFactory__InvalidHooksParameters();
error LBFactory__CannotGrantDefaultAdminRole();
/**
* @dev Structure to store the LBPair information, such as:
* binStep: The bin step of the LBPair
* LBPair: The address of the LBPair
* createdByOwner: Whether the pair was created by the owner of the factory
* ignoredForRouting: Whether the pair is ignored for routing or not. An ignored pair will not be explored during routes finding
*/
struct LBPairInformation {
uint16 binStep;
ILBPair LBPair;
bool createdByOwner;
bool ignoredForRouting;
}
event LBPairCreated(
IERC20 indexed tokenX, IERC20 indexed tokenY, uint256 indexed binStep, ILBPair LBPair, uint256 pid
);
event FeeRecipientSet(address oldRecipient, address newRecipient);
event FlashLoanFeeSet(uint256 oldFlashLoanFee, uint256 newFlashLoanFee);
event LBPairImplementationSet(address oldLBPairImplementation, address LBPairImplementation);
event LBPairIgnoredStateChanged(ILBPair indexed LBPair, bool ignored);
event PresetSet(
uint256 indexed binStep,
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxVolatilityAccumulator
);
event PresetOpenStateChanged(uint256 indexed binStep, bool indexed isOpen);
event PresetRemoved(uint256 indexed binStep);
event QuoteAssetAdded(IERC20 indexed quoteAsset);
event QuoteAssetRemoved(IERC20 indexed quoteAsset);
function getMinBinStep() external pure returns (uint256);
function getFeeRecipient() external view returns (address);
function getMaxFlashLoanFee() external pure returns (uint256);
function getFlashLoanFee() external view returns (uint256);
function getLBPairImplementation() external view returns (address);
function getNumberOfLBPairs() external view returns (uint256);
function getLBPairAtIndex(uint256 id) external returns (ILBPair);
function getNumberOfQuoteAssets() external view returns (uint256);
function getQuoteAssetAtIndex(uint256 index) external view returns (IERC20);
function isQuoteAsset(IERC20 token) external view returns (bool);
function getLBPairInformation(IERC20 tokenX, IERC20 tokenY, uint256 binStep)
external
view
returns (LBPairInformation memory);
function getPreset(uint256 binStep)
external
view
returns (
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxAccumulator,
bool isOpen
);
function getAllBinSteps() external view returns (uint256[] memory presetsBinStep);
function getOpenBinSteps() external view returns (uint256[] memory openBinStep);
function getAllLBPairs(IERC20 tokenX, IERC20 tokenY)
external
view
returns (LBPairInformation[] memory LBPairsBinStep);
function setLBPairImplementation(address lbPairImplementation) external;
function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
external
returns (ILBPair pair);
function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint16 binStep, bool ignored) external;
function setPreset(
uint16 binStep,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator,
bool isOpen
) external;
function setPresetOpenState(uint16 binStep, bool isOpen) external;
function removePreset(uint16 binStep) external;
function setFeesParametersOnPair(
IERC20 tokenX,
IERC20 tokenY,
uint16 binStep,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) external;
function setLBHooksParametersOnPair(
IERC20 tokenX,
IERC20 tokenY,
uint16 binStep,
bytes32 hooksParameters,
bytes memory onHooksSetData
) external;
function removeLBHooksOnPair(IERC20 tokenX, IERC20 tokenY, uint16 binStep) external;
function setFeeRecipient(address feeRecipient) external;
function setFlashLoanFee(uint256 flashLoanFee) external;
function addQuoteAsset(IERC20 quoteAsset) external;
function removeQuoteAsset(IERC20 quoteAsset) external;
function forceDecay(ILBPair lbPair) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Hooks} from "../libraries/Hooks.sol";
import {ILBFactory} from "./ILBFactory.sol";
import {ILBFlashLoanCallback} from "./ILBFlashLoanCallback.sol";
import {ILBToken} from "./ILBToken.sol";
interface ILBPair is ILBToken {
error LBPair__ZeroBorrowAmount();
error LBPair__AddressZero();
error LBPair__EmptyMarketConfigs();
error LBPair__FlashLoanCallbackFailed();
error LBPair__FlashLoanInsufficientAmount();
error LBPair__InsufficientAmountIn();
error LBPair__InsufficientAmountOut();
error LBPair__InvalidInput();
error LBPair__InvalidStaticFeeParameters();
error LBPair__OnlyFactory();
error LBPair__OnlyProtocolFeeRecipient();
error LBPair__OutOfLiquidity();
error LBPair__TokenNotSupported();
error LBPair__ZeroAmount(uint24 id);
error LBPair__ZeroAmountsOut(uint24 id);
error LBPair__ZeroShares(uint24 id);
error LBPair__MaxTotalFeeExceeded();
error LBPair__InvalidHooks();
struct MintArrays {
uint256[] ids;
bytes32[] amounts;
uint256[] liquidityMinted;
}
event DepositedToBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts);
event WithdrawnFromBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts);
event CompositionFees(address indexed sender, uint24 id, bytes32 totalFees, bytes32 protocolFees);
event CollectedProtocolFees(address indexed feeRecipient, bytes32 protocolFees);
event Swap(
address indexed sender,
address indexed to,
uint24 id,
bytes32 amountsIn,
bytes32 amountsOut,
uint24 volatilityAccumulator,
bytes32 totalFees,
bytes32 protocolFees
);
event StaticFeeParametersSet(
address indexed sender,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
);
event HooksParametersSet(address indexed sender, bytes32 hooksParameters);
event FlashLoan(
address indexed sender,
ILBFlashLoanCallback indexed receiver,
uint24 activeId,
bytes32 amounts,
bytes32 totalFees,
bytes32 protocolFees
);
event OracleLengthIncreased(address indexed sender, uint16 oracleLength);
event ForcedDecay(address indexed sender, uint24 idReference, uint24 volatilityReference);
function initialize(
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator,
uint24 activeId
) external;
function implementation() external view returns (address);
function getFactory() external view returns (ILBFactory factory);
function getTokenX() external view returns (IERC20 tokenX);
function getTokenY() external view returns (IERC20 tokenY);
function getBinStep() external view returns (uint16 binStep);
function getReserves() external view returns (uint128 reserveX, uint128 reserveY);
function getActiveId() external view returns (uint24 activeId);
function getBin(uint24 id) external view returns (uint128 binReserveX, uint128 binReserveY);
function getNextNonEmptyBin(bool swapForY, uint24 id) external view returns (uint24 nextId);
function getProtocolFees() external view returns (uint128 protocolFeeX, uint128 protocolFeeY);
function getStaticFeeParameters()
external
view
returns (
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
);
function getLBHooksParameters() external view returns (bytes32 hooksParameters);
function getVariableFeeParameters()
external
view
returns (uint24 volatilityAccumulator, uint24 volatilityReference, uint24 idReference, uint40 timeOfLastUpdate);
function getOracleParameters()
external
view
returns (uint8 sampleLifetime, uint16 size, uint16 activeSize, uint40 lastUpdated, uint40 firstTimestamp);
function getOracleSampleAt(uint40 lookupTimestamp)
external
view
returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed);
function getPriceFromId(uint24 id) external view returns (uint256 price);
function getIdFromPrice(uint256 price) external view returns (uint24 id);
function getSwapIn(uint128 amountOut, bool swapForY)
external
view
returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee);
function getSwapOut(uint128 amountIn, bool swapForY)
external
view
returns (uint128 amountInLeft, uint128 amountOut, uint128 fee);
function swap(bool swapForY, address to) external returns (bytes32 amountsOut);
function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external;
function mint(address to, bytes32[] calldata liquidityConfigs, address refundTo)
external
returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted);
function burn(address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn)
external
returns (bytes32[] memory amounts);
function collectProtocolFees() external returns (bytes32 collectedProtocolFees);
function increaseOracleLength(uint16 newLength) external;
function setStaticFeeParameters(
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) external;
function setHooksParameters(bytes32 hooksParameters, bytes calldata onHooksSetData) external;
function forceDecay() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ILBToken} from "./interfaces/ILBToken.sol";
/**
* @title Liquidity Book Token
* @author Trader Joe
* @notice The LBToken is an implementation of a multi-token.
* It allows to create multi-ERC20 represented by their ids.
* Its implementation is really similar to the ERC1155 standard the main difference
* is that it doesn't do any call to the receiver contract to prevent reentrancy.
* As it's only for ERC20s, the uri function is not implemented.
* The contract is made for batch operations.
*/
abstract contract LBToken is ILBToken {
/**
* @dev The mapping from account to token id to account balance.
*/
mapping(address => mapping(uint256 => uint256)) private _balances;
/**
* @dev The mapping from token id to total supply.
*/
mapping(uint256 => uint256) private _totalSupplies;
/**
* @dev Mapping from account to spender approvals.
*/
mapping(address => mapping(address => bool)) private _spenderApprovals;
/**
* @dev Modifier to check if the spender is approved for all.
*/
modifier checkApproval(address from, address spender) {
if (!_isApprovedForAll(from, spender)) revert LBToken__SpenderNotApproved(from, spender);
_;
}
/**
* @dev Modifier to check if the address is not zero or the contract itself.
*/
modifier notAddressZeroOrThis(address account) {
_notAddressZeroOrThis(account);
_;
}
/**
* @dev Modifier to check if the length of the arrays are equal.
*/
modifier checkLength(uint256 lengthA, uint256 lengthB) {
_checkLength(lengthA, lengthB);
_;
}
/**
* @notice Returns the name of the token.
* @return The name of the token.
*/
function name() public view virtual override returns (string memory) {
return "Liquidity Book Token";
}
/**
* @notice Returns the symbol of the token, usually a shorter version of the name.
* @return The symbol of the token.
*/
function symbol() public view virtual override returns (string memory) {
return "LBT";
}
/**
* @notice Returns the total supply of token of type `id`.
* /**
* @dev This is the amount of token of type `id` minted minus the amount burned.
* @param id The token id.
* @return The total supply of that token id.
*/
function totalSupply(uint256 id) public view virtual override returns (uint256) {
return _totalSupplies[id];
}
/**
* @notice Returns the amount of tokens of type `id` owned by `account`.
* @param account The address of the owner.
* @param id The token id.
* @return The amount of tokens of type `id` owned by `account`.
*/
function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
return _balances[account][id];
}
/**
* @notice Return the balance of multiple (account/id) pairs.
* @param accounts The addresses of the owners.
* @param ids The token ids.
* @return batchBalances The balance for each (account, id) pair.
*/
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
public
view
virtual
override
checkLength(accounts.length, ids.length)
returns (uint256[] memory batchBalances)
{
batchBalances = new uint256[](accounts.length);
unchecked {
for (uint256 i; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts[i], ids[i]);
}
}
}
/**
* @notice Returns true if `spender` is approved to transfer `owner`'s tokens or if `spender` is the `owner`.
* @param owner The address of the owner.
* @param spender The address of the spender.
* @return True if `spender` is approved to transfer `owner`'s tokens.
*/
function isApprovedForAll(address owner, address spender) public view virtual override returns (bool) {
return _isApprovedForAll(owner, spender);
}
/**
* @notice Grants or revokes permission to `spender` to transfer the caller's lbTokens, according to `approved`.
* @param spender The address of the spender.
* @param approved The boolean value to grant or revoke permission.
*/
function approveForAll(address spender, bool approved) public virtual override {
_approveForAll(msg.sender, spender, approved);
}
/**
* @notice Batch transfers `amounts` of `ids` from `from` to `to`.
* @param from The address of the owner.
* @param to The address of the recipient.
* @param ids The list of token ids.
* @param amounts The list of amounts to transfer for each token id in `ids`.
*/
function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts)
public
virtual
override
checkApproval(from, msg.sender)
{
_batchTransferFrom(from, to, ids, amounts);
}
/**
* @notice Returns true if `spender` is approved to transfer `owner`'s tokens or if `spender` is the `owner`.
* @param owner The address of the owner.
* @param spender The address of the spender.
* @return True if `spender` is approved to transfer `owner`'s tokens.
*/
function _isApprovedForAll(address owner, address spender) internal view returns (bool) {
return owner == spender || _spenderApprovals[owner][spender];
}
/**
* @dev Mint `amount` of `id` to `account`.
* The `account` must not be the zero address.
* The event should be emitted by the contract that inherits this contract.
* @param account The address of the owner.
* @param id The token id.
* @param amount The amount to mint.
*/
function _mint(address account, uint256 id, uint256 amount) internal {
_totalSupplies[id] += amount;
unchecked {
_balances[account][id] += amount;
}
}
/**
* @dev Burn `amount` of `id` from `account`.
* The `account` must not be the zero address.
* The event should be emitted by the contract that inherits this contract.
* @param account The address of the owner.
* @param id The token id.
* @param amount The amount to burn.
*/
function _burn(address account, uint256 id, uint256 amount) internal {
mapping(uint256 => uint256) storage accountBalances = _balances[account];
uint256 balance = accountBalances[id];
if (balance < amount) revert LBToken__BurnExceedsBalance(account, id, amount);
unchecked {
_totalSupplies[id] -= amount;
accountBalances[id] = balance - amount;
}
}
/**
* @dev Batch transfers `amounts` of `ids` from `from` to `to`.
* The `to` must not be the zero address and the `ids` and `amounts` must have the same length.
* @param from The address of the owner.
* @param to The address of the recipient.
* @param ids The list of token ids.
* @param amounts The list of amounts to transfer for each token id in `ids`.
*/
function _batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts)
internal
checkLength(ids.length, amounts.length)
notAddressZeroOrThis(to)
{
mapping(uint256 => uint256) storage fromBalances = _balances[from];
mapping(uint256 => uint256) storage toBalances = _balances[to];
for (uint256 i; i < ids.length;) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 fromBalance = fromBalances[id];
if (fromBalance < amount) revert LBToken__TransferExceedsBalance(from, id, amount);
unchecked {
fromBalances[id] = fromBalance - amount;
toBalances[id] += amount;
++i;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
}
/**
* @notice Grants or revokes permission to `spender` to transfer the caller's tokens, according to `approved`
* @param owner The address of the owner
* @param spender The address of the spender
* @param approved The boolean value to grant or revoke permission
*/
function _approveForAll(address owner, address spender, bool approved) internal notAddressZeroOrThis(owner) {
if (owner == spender) revert LBToken__SelfApproval(owner);
_spenderApprovals[owner][spender] = approved;
emit ApprovalForAll(owner, spender, approved);
}
/**
* @dev Reverts if the address is the zero address or the contract itself.
* @param account The address to check.
*/
function _notAddressZeroOrThis(address account) internal view {
if (account == address(0) || account == address(this)) revert LBToken__AddressThisOrZero();
}
/**
* @dev Reverts if the length of the arrays are not equal.
* @param lengthA The length of the first array.
* @param lengthB The length of the second array.
*/
function _checkLength(uint256 lengthA, uint256 lengthB) internal pure {
if (lengthA != lengthB) revert LBToken__InvalidLength();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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)
*/
library PairParameterHelper {
using SafeCast for uint256;
using Encoded for bytes32;
error PairParametersHelper__InvalidParameter();
uint256 internal constant OFFSET_BASE_FACTOR = 0;
uint256 internal constant OFFSET_FILTER_PERIOD = 16;
uint256 internal constant OFFSET_DECAY_PERIOD = 28;
uint256 internal constant OFFSET_REDUCTION_FACTOR = 40;
uint256 internal constant OFFSET_VAR_FEE_CONTROL = 54;
uint256 internal constant OFFSET_PROTOCOL_SHARE = 78;
uint256 internal constant OFFSET_MAX_VOL_ACC = 92;
uint256 internal constant OFFSET_VOL_ACC = 112;
uint256 internal constant OFFSET_VOL_REF = 132;
uint256 internal constant OFFSET_ID_REF = 152;
uint256 internal constant OFFSET_TIME_LAST_UPDATE = 176;
uint256 internal constant OFFSET_ORACLE_ID = 216;
uint256 internal constant OFFSET_ACTIVE_ID = 232;
uint256 internal constant 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
*/
function getBaseFactor(bytes32 params) internal pure returns (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
*/
function getFilterPeriod(bytes32 params) internal pure returns (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
*/
function getDecayPeriod(bytes32 params) internal pure returns (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
*/
function getReductionFactor(bytes32 params) internal pure returns (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
*/
function getVariableFeeControl(bytes32 params) internal pure returns (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
*/
function getProtocolShare(bytes32 params) internal pure returns (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
*/
function getMaxVolatilityAccumulator(bytes32 params) internal pure returns (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
*/
function getVolatilityAccumulator(bytes32 params) internal pure returns (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
*/
function getVolatilityReference(bytes32 params) internal pure returns (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
*/
function getIdReference(bytes32 params) internal pure returns (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
*/
function getTimeOfLastUpdate(bytes32 params) internal pure returns (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
*/
function getOracleId(bytes32 params) internal pure returns (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
*/
function getActiveId(bytes32 params) internal pure returns (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
*/
function getDeltaId(bytes32 params, uint24 activeId) internal pure returns (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
*/
function getBaseFee(bytes32 params, uint16 binStep) internal pure returns (uint256) {
unchecked {
// Base factor is in basis points, binStep is in basis points, so we multiply by 1e10
return uint256(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
*/
function getVariableFee(bytes32 params, uint16 binStep) internal pure returns (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 100e18th
uint256 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
*/
function getTotalFee(bytes32 params, uint16 binStep) internal pure returns (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
*/
function setOracleId(bytes32 params, uint16 oracleId) internal pure returns (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
*/
function setVolatilityReference(bytes32 params, uint24 volRef) internal pure returns (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
*/
function setVolatilityAccumulator(bytes32 params, uint24 volAcc) internal pure returns (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
*/
function setActiveId(bytes32 params, uint24 activeId) internal pure returns (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
*/
function setStaticFeeParameters(
bytes32 params,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) internal pure returns (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
*/
function updateIdReference(bytes32 params) internal pure returns (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
*/
function updateTimeOfLastUpdate(bytes32 params, uint256 timestamp) internal pure returns (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
*/
function updateVolatilityReference(bytes32 params) internal pure returns (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
*/
function updateVolatilityAccumulator(bytes32 params, uint24 activeId) internal pure returns (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
*/
function updateReferences(bytes32 params, uint256 timestamp) internal pure returns (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
*/
function updateVolatilityParameters(bytes32 params, uint24 activeId, uint256 timestamp)
internal
pure
returns (bytes32)
{
params = updateReferences(params, timestamp);
return updateVolatilityAccumulator(params, activeId);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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
*/
library PriceHelper {
using Uint128x128Math for uint256;
using Uint256x256Math for uint256;
using SafeCast for uint256;
int256 private constant 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
*/
function getPriceFromId(uint24 id, uint16 binStep) internal pure returns (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
*/
function getIdFromPrice(uint256 price, uint16 binStep) internal pure returns (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
*/
function getBase(uint16 binStep) internal pure returns (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
*/
function getExponent(uint24 id) internal pure returns (int256) {
unchecked {
return int256(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
*/
function convertDecimalPriceTo128x128(uint256 price) internal pure returns (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
*/
function convert128x128PriceToDecimal(uint256 price128x128) internal pure returns (uint256) {
return price128x128.mulShiftRoundDown(Constants.PRECISION, Constants.SCALE_OFFSET);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {BitMath} from "./BitMath.sol";
/**
* @title Liquidity Book Tree Math Library
* @author Trader Joe
* @notice This library contains functions to interact with a tree of TreeUint24.
*/
library TreeMath {
using BitMath for uint256;
struct TreeUint24 {
bytes32 level0;
mapping(bytes32 => bytes32) level1;
mapping(bytes32 => bytes32) level2;
}
/**
* @dev Returns true if the tree contains the id
* @param tree The tree
* @param id The id
* @return True if the tree contains the id
*/
function contains(TreeUint24 storage tree, uint24 id) internal view returns (bool) {
bytes32 leaf2 = bytes32(uint256(id) >> 8);
return tree.level2[leaf2] & bytes32(1 << (id & type(uint8).max)) != 0;
}
/**
* @dev Adds the id to the tree and returns true if the id was not already in the tree
* It will also propagate the change to the parent levels.
* @param tree The tree
* @param id The id
* @return True if the id was not already in the tree
*/
function add(TreeUint24 storage tree, uint24 id) internal returns (bool) {
bytes32 key2 = bytes32(uint256(id) >> 8);
bytes32 leaves = tree.level2[key2];
bytes32 newLeaves = leaves | bytes32(1 << (id & type(uint8).max));
if (leaves != newLeaves) {
tree.level2[key2] = newLeaves;
if (leaves == 0) {
bytes32 key1 = key2 >> 8;
leaves = tree.level1[key1];
tree.level1[key1] = leaves | bytes32(1 << (uint256(key2) & type(uint8).max));
if (leaves == 0) tree.level0 |= bytes32(1 << (uint256(key1) & type(uint8).max));
}
return true;
}
return false;
}
/**
* @dev Removes the id from the tree and returns true if the id was in the tree.
* It will also propagate the change to the parent levels.
* @param tree The tree
* @param id The id
* @return True if the id was in the tree
*/
function remove(TreeUint24 storage tree, uint24 id) internal returns (bool) {
bytes32 key2 = bytes32(uint256(id) >> 8);
bytes32 leaves = tree.level2[key2];
bytes32 newLeaves = leaves & ~bytes32(1 << (id & type(uint8).max));
if (leaves != newLeaves) {
tree.level2[key2] = newLeaves;
if (newLeaves == 0) {
bytes32 key1 = key2 >> 8;
newLeaves = tree.level1[key1] & ~bytes32(1 << (uint256(key2) & type(uint8).max));
tree.level1[key1] = newLeaves;
if (newLeaves == 0) tree.level0 &= ~bytes32(1 << (uint256(key1) & type(uint8).max));
}
return true;
}
return false;
}
/**
* @dev Returns the first id in the tree that is lower than or equal to the given id.
* It will return type(uint24).max if there is no such id.
* @param tree The tree
* @param id The id
* @return The first id in the tree that is lower than the given id
*/
function findFirstRight(TreeUint24 storage tree, uint24 id) internal view returns (uint24) {
bytes32 leaves;
bytes32 key2 = bytes32(uint256(id) >> 8);
uint8 bit = uint8(id & type(uint8).max);
if (bit != 0) {
leaves = tree.level2[key2];
uint256 closestBit = _closestBitRight(leaves, bit);
if (closestBit != type(uint256).max) return uint24(uint256(key2) << 8 | closestBit);
}
bytes32 key1 = key2 >> 8;
bit = uint8(uint256(key2) & type(uint8).max);
if (bit != 0) {
leaves = tree.level1[key1];
uint256 closestBit = _closestBitRight(leaves, bit);
if (closestBit != type(uint256).max) {
key2 = bytes32(uint256(key1) << 8 | closestBit);
leaves = tree.level2[key2];
return uint24(uint256(key2) << 8 | uint256(leaves).mostSignificantBit());
}
}
bit = uint8(uint256(key1) & type(uint8).max);
if (bit != 0) {
leaves = tree.level0;
uint256 closestBit = _closestBitRight(leaves, bit);
if (closestBit != type(uint256).max) {
key1 = bytes32(closestBit);
leaves = tree.level1[key1];
key2 = bytes32(uint256(key1) << 8 | uint256(leaves).mostSignificantBit());
leaves = tree.level2[key2];
return uint24(uint256(key2) << 8 | uint256(leaves).mostSignificantBit());
}
}
return type(uint24).max;
}
/**
* @dev Returns the first id in the tree that is higher than or equal to the given id.
* It will return 0 if there is no such id.
* @param tree The tree
* @param id The id
* @return The first id in the tree that is higher than the given id
*/
function findFirstLeft(TreeUint24 storage tree, uint24 id) internal view returns (uint24) {
bytes32 leaves;
bytes32 key2 = bytes32(uint256(id) >> 8);
uint8 bit = uint8(id & type(uint8).max);
if (bit != type(uint8).max) {
leaves = tree.level2[key2];
uint256 closestBit = _closestBitLeft(leaves, bit);
if (closestBit != type(uint256).max) return uint24(uint256(key2) << 8 | closestBit);
}
bytes32 key1 = key2 >> 8;
bit = uint8(uint256(key2) & type(uint8).max);
if (bit != type(uint8).max) {
leaves = tree.level1[key1];
uint256 closestBit = _closestBitLeft(leaves, bit);
if (closestBit != type(uint256).max) {
key2 = bytes32(uint256(key1) << 8 | closestBit);
leaves = tree.level2[key2];
return uint24(uint256(key2) << 8 | uint256(leaves).leastSignificantBit());
}
}
bit = uint8(uint256(key1) & type(uint8).max);
if (bit != type(uint8).max) {
leaves = tree.level0;
uint256 closestBit = _closestBitLeft(leaves, bit);
if (closestBit != type(uint256).max) {
key1 = bytes32(closestBit);
leaves = tree.level1[key1];
key2 = bytes32(uint256(key1) << 8 | uint256(leaves).leastSignificantBit());
leaves = tree.level2[key2];
return uint24(uint256(key2) << 8 | uint256(leaves).leastSignificantBit());
}
}
return 0;
}
/**
* @dev Returns the first bit in the given leaves that is strictly lower than the given bit.
* It will return type(uint256).max if there is no such bit.
* @param leaves The leaves
* @param bit The bit
* @return The first bit in the given leaves that is strictly lower than the given bit
*/
function _closestBitRight(bytes32 leaves, uint8 bit) private pure returns (uint256) {
unchecked {
return uint256(leaves).closestBitRight(bit - 1);
}
}
/**
* @dev Returns the first bit in the given leaves that is strictly higher than the given bit.
* It will return type(uint256).max if there is no such bit.
* @param leaves The leaves
* @param bit The bit
* @return The first bit in the given leaves that is strictly higher than the given bit
*/
function _closestBitLeft(bytes32 leaves, uint8 bit) private pure returns (uint256) {
unchecked {
return uint256(leaves).closestBitLeft(bit + 1);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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
*/
library Uint256x256Math {
error Uint256x256Math__MulShiftOverflow();
error Uint256x256Math__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
*/
function mulDivRoundDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (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
*/
function mulDivRoundUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (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
*/
function mulShiftRoundDown(uint256 x, uint256 y, uint8 offset) internal pure returns (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
*/
function mulShiftRoundUp(uint256 x, uint256 y, uint8 offset) internal pure returns (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
*/
function shiftDivRoundDown(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) {
uint256 prod0;
uint256 prod1;
prod0 = x << offset; // Least significant 256 bits of the product
unchecked {
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
*/
function shiftDivRoundUp(uint256 x, uint8 offset, uint256 denominator) internal pure returns (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) private pure returns (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)
private
pure
returns (uint256 result)
{
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
unchecked {
result = prod0 / denominator;
}
} else {
// Make sure the result is less than 2^256. Also prevents denominator == 0
if (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/92363
unchecked {
// Does not overflow because the denominator cannot be zero at this stage in the function
uint256 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^4
uint256 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
*/
function sqrt(uint256 x) internal pure returns (uint256 sqrtX) {
if (x == 0) return 0;
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;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title Liquidity Book Flashloan Callback Interface
/// @author Trader Joe
/// @notice Required interface to interact with LB flash loans
interface ILBFlashLoanCallback {
function LBFlashLoanCallback(
address sender,
IERC20 tokenX,
IERC20 tokenY,
bytes32 amounts,
bytes32 totalFees,
bytes calldata data
) external returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^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(...)`
*/
library TokenHelper {
error TokenHelper__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
*/
function safeTransferFrom(IERC20 token, address owner, address recipient, uint256 amount) internal {
bytes memory 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
*/
function safeTransfer(IERC20 token, address recipient, uint256 amount) internal {
bytes memory data = abi.encodeWithSelector(token.transfer.selector, recipient, amount);
_callAndCatch(token, data);
}
function _callAndCatch(IERC20 token, bytes memory data) internal {
bool success;
assembly {
mstore(0x00, 0)
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0x00, 0x20)
switch success
case 0 {
if returndatasize() {
returndatacopy(0x00, 0x00, returndatasize())
revert(0x00, returndatasize())
}
}
default {
switch returndatasize()
case 0 { success := iszero(iszero(extcodesize(token))) }
default { success := and(success, eq(mload(0x00), 1)) }
}
}
if (!success) revert TokenHelper__TransferFailed();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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.
*/
library BinHelper {
using PackedUint128Math for bytes32;
using PackedUint128Math for uint128;
using Uint256x256Math for uint256;
using PriceHelper for uint24;
using SafeCast for uint256;
using PairParameterHelper for bytes32;
using FeeHelper for uint128;
using TokenHelper for IERC20;
error BinHelper__CompositionFactorFlawed(uint24 id);
error BinHelper__LiquidityOverflow();
error BinHelper__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
*/
function getAmountOutOfBin(bytes32 binReserves, uint256 amountToBurn, uint256 totalSupply)
internal
pure
returns (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.
*/
function getSharesAndEffectiveAmountsIn(bytes32 binReserves, bytes32 amountsIn, uint256 price, uint256 totalSupply)
internal
pure
returns (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 valuable
if (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
*/
function getLiquidity(bytes32 amounts, uint256 price) internal pure returns (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
*/
function getLiquidity(uint256 x, uint256 y, uint256 price) internal pure returns (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
*/
function verifyAmounts(bytes32 amounts, uint24 activeId, uint24 id) internal pure {
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
*/
function getCompositionFees(
bytes32 binReserves,
bytes32 parameters,
uint16 binStep,
bytes32 amountsIn,
uint256 totalSupply,
uint256 shares
) internal pure returns (bytes32 fees) {
if (shares == 0) return 0;
(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();
} else if (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)
*/
function isEmpty(bytes32 binReserves, bool isX) internal pure returns (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
*/
function getAmounts(
bytes32 binReserves,
bytes32 parameters,
uint16 binStep,
bool swapForY, // swap `swapForY` and `activeId` to avoid stack too deep
uint24 activeId,
bytes32 amountsInLeft
) internal pure returns (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
*/
function received(bytes32 reserves, IERC20 tokenX, IERC20 tokenY) internal view returns (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
*/
function receivedX(bytes32 reserves, IERC20 tokenX) internal view returns (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
*/
function receivedY(bytes32 reserves, IERC20 tokenY) internal view returns (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
*/
function transfer(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
*/
function transferX(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
*/
function transferY(bytes32 amounts, IERC20 tokenY, address recipient) internal {
uint128 amountY = amounts.decodeY();
if (amountY > 0) tokenY.safeTransfer(recipient, amountY);
}
function _balanceOf(IERC20 token) private view returns (uint128) {
return token.balanceOf(address(this)).safe128();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Clone
* @notice Class with helper read functions for clone with immutable args.
* @author Trader Joe
* @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Clone.sol)
* @author Adapted from clones with immutable args by zefram.eth, Saw-mon & Natalie
* (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
*/
abstract contract Clone {
/**
* @dev Reads an immutable arg with type bytes
* @param argOffset The offset of the arg in the immutable args
* @param length The length of the arg
* @return arg The immutable bytes arg
*/
function _getArgBytes(uint256 argOffset, uint256 length) internal pure returns (bytes memory arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
arg := mload(0x40)
// Store the array length.
mstore(arg, length)
// Copy the array.
calldatacopy(add(arg, 0x20), add(offset, argOffset), length)
// Allocate the memory, rounded up to the next 32 byte boundary.
mstore(0x40, and(add(add(arg, 0x3f), length), not(0x1f)))
}
}
/**
* @dev Reads an immutable arg with type address
* @param argOffset The offset of the arg in the immutable args
* @return arg The immutable address arg
*/
function _getArgAddress(uint256 argOffset) internal pure returns (address arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(0x60, calldataload(add(offset, argOffset)))
}
}
/**
* @dev Reads an immutable arg with type uint256
* @param argOffset The offset of the arg in the immutable args
* @return arg The immutable uint256 arg
*/
function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := calldataload(add(offset, argOffset))
}
}
/**
* @dev Reads a uint256 array stored in the immutable args.
* @param argOffset The offset of the arg in the immutable args
* @param length The length of the arg
* @return arg The immutable uint256 array arg
*/
function _getArgUint256Array(uint256 argOffset, uint256 length) internal pure returns (uint256[] memory arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
arg := mload(0x40)
// Store the array length.
mstore(arg, length)
// Copy the array.
calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length))
// Allocate the memory.
mstore(0x40, add(add(arg, 0x20), shl(5, length)))
}
}
/**
* @dev Reads an immutable arg with type uint64
* @param argOffset The offset of the arg in the immutable args
* @return arg The immutable uint64 arg
*/
function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(0xc0, calldataload(add(offset, argOffset)))
}
}
/**
* @dev Reads an immutable arg with type uint16
* @param argOffset The offset of the arg in the immutable args
* @return arg The immutable uint16 arg
*/
function _getArgUint16(uint256 argOffset) internal pure returns (uint16 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(0xf0, calldataload(add(offset, argOffset)))
}
}
/**
* @dev Reads an immutable arg with type uint8
* @param argOffset The offset of the arg in the immutable args
* @return arg The immutable uint8 arg
*/
function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
uint256 offset = _getImmutableArgsOffset();
/// @solidity memory-safe-assembly
assembly {
arg := shr(0xf8, calldataload(add(offset, argOffset)))
}
}
/**
* @dev Reads the offset of the packed immutable args in calldata.
* @return offset The offset of the packed immutable args in calldata.
*/
function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
/// @solidity memory-safe-assembly
assembly {
offset := sub(calldatasize(), shr(0xf0, calldataload(sub(calldatasize(), 2))))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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.
*/
library LiquidityConfigurations {
using PackedUint128Math for bytes32;
using PackedUint128Math for uint128;
using Encoded for bytes32;
error LiquidityConfigurations__InvalidConfig();
uint256 private constant OFFSET_ID = 0;
uint256 private constant OFFSET_DISTRIBUTION_Y = 24;
uint256 private constant OFFSET_DISTRIBUTION_X = 88;
uint256 private constant 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
*/
function encodeParams(uint64 distributionX, uint64 distributionY, uint24 id)
internal
pure
returns (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
*/
function decodeParams(bytes32 config)
internal
pure
returns (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
*/
function getAmountsAndId(bytes32 config, bytes32 amountsIn) internal pure returns (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);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/**
* @dev This contract is a fork of the `ReentrancyGuardUpgradeable` contract from OpenZeppelin
* that has been modified to update the `_nonReentrantBefore` and `_nonReentrantAfter`
* functions to `internal` visibility.
*
* Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() internal {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() internal {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Encoded Library
* @author Trader Joe
* @notice Helper contract used for decoding bytes32 sample
*/
library Encoded {
uint256 internal constant MASK_UINT1 = 0x1;
uint256 internal constant MASK_UINT8 = 0xff;
uint256 internal constant MASK_UINT12 = 0xfff;
uint256 internal constant MASK_UINT14 = 0x3fff;
uint256 internal constant MASK_UINT16 = 0xffff;
uint256 internal constant MASK_UINT20 = 0xfffff;
uint256 internal constant MASK_UINT24 = 0xffffff;
uint256 internal constant MASK_UINT40 = 0xffffffffff;
uint256 internal constant MASK_UINT64 = 0xffffffffffffffff;
uint256 internal constant 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
*/
function set(bytes32 encoded, uint256 value, uint256 mask, uint256 offset)
internal
pure
returns (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
*/
function setBool(bytes32 encoded, bool boolean, uint256 offset) internal pure returns (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
*/
function decode(bytes32 encoded, uint256 mask, uint256 offset) internal pure returns (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
*/
function decodeBool(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint8(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint12(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint14(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint16(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint20(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint24(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint40(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint64(bytes32 encoded, uint256 offset) internal pure returns (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
*/
function decodeUint128(bytes32 encoded, uint256 offset) internal pure returns (uint128 value) {
assembly {
value := and(shr(offset, encoded), MASK_UINT128)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {Encoded} from "./Encoded.sol";
/**
* @title Liquidity Book Sample Math Library
* @author Trader Joe
* @notice This library contains functions to encode and decode a sample into a single bytes32
* and interact with the encoded bytes32
* The sample is encoded as follows:
* 0 - 16: oracle length (16 bits)
* 16 - 80: cumulative id (64 bits)
* 80 - 144: cumulative volatility accumulator (64 bits)
* 144 - 208: cumulative bin crossed (64 bits)
* 208 - 216: sample lifetime (8 bits)
* 216 - 256: sample creation timestamp (40 bits)
*/
library SampleMath {
using Encoded for bytes32;
uint256 internal constant OFFSET_ORACLE_LENGTH = 0;
uint256 internal constant OFFSET_CUMULATIVE_ID = 16;
uint256 internal constant OFFSET_CUMULATIVE_VOLATILITY = 80;
uint256 internal constant OFFSET_CUMULATIVE_BIN_CROSSED = 144;
uint256 internal constant OFFSET_SAMPLE_LIFETIME = 208;
uint256 internal constant OFFSET_SAMPLE_CREATION = 216;
/**
* @dev Encodes a sample
* @param oracleLength The oracle length
* @param cumulativeId The cumulative id
* @param cumulativeVolatility The cumulative volatility
* @param cumulativeBinCrossed The cumulative bin crossed
* @param sampleLifetime The sample lifetime
* @param createdAt The sample creation timestamp
* @return sample The encoded sample
*/
function encode(
uint16 oracleLength,
uint64 cumulativeId,
uint64 cumulativeVolatility,
uint64 cumulativeBinCrossed,
uint8 sampleLifetime,
uint40 createdAt
) internal pure returns (bytes32 sample) {
sample = sample.set(oracleLength, Encoded.MASK_UINT16, OFFSET_ORACLE_LENGTH);
sample = sample.set(cumulativeId, Encoded.MASK_UINT64, OFFSET_CUMULATIVE_ID);
sample = sample.set(cumulativeVolatility, Encoded.MASK_UINT64, OFFSET_CUMULATIVE_VOLATILITY);
sample = sample.set(cumulativeBinCrossed, Encoded.MASK_UINT64, OFFSET_CUMULATIVE_BIN_CROSSED);
sample = sample.set(sampleLifetime, Encoded.MASK_UINT8, OFFSET_SAMPLE_LIFETIME);
sample = sample.set(createdAt, Encoded.MASK_UINT40, OFFSET_SAMPLE_CREATION);
}
/**
* @dev Gets the oracle length from an encoded sample
* @param sample The encoded sample as follows:
* [0 - 16[: oracle length (16 bits)
* [16 - 256[: any (240 bits)
* @return length The oracle length
*/
function getOracleLength(bytes32 sample) internal pure returns (uint16 length) {
return sample.decodeUint16(0);
}
/**
* @dev Gets the cumulative id from an encoded sample
* @param sample The encoded sample as follows:
* [0 - 16[: any (16 bits)
* [16 - 80[: cumulative id (64 bits)
* [80 - 256[: any (176 bits)
* @return id The cumulative id
*/
function getCumulativeId(bytes32 sample) internal pure returns (uint64 id) {
return sample.decodeUint64(OFFSET_CUMULATIVE_ID);
}
/**
* @dev Gets the cumulative volatility accumulator from an encoded sample
* @param sample The encoded sample as follows:
* [0 - 80[: any (80 bits)
* [80 - 144[: cumulative volatility accumulator (64 bits)
* [144 - 256[: any (112 bits)
* @return volatilityAccumulator The cumulative volatility
*/
function getCumulativeVolatility(bytes32 sample) internal pure returns (uint64 volatilityAccumulator) {
return sample.decodeUint64(OFFSET_CUMULATIVE_VOLATILITY);
}
/**
* @dev Gets the cumulative bin crossed from an encoded sample
* @param sample The encoded sample as follows:
* [0 - 144[: any (144 bits)
* [144 - 208[: cumulative bin crossed (64 bits)
* [208 - 256[: any (48 bits)
* @return binCrossed The cumulative bin crossed
*/
function getCumulativeBinCrossed(bytes32 sample) internal pure returns (uint64 binCrossed) {
return sample.decodeUint64(OFFSET_CUMULATIVE_BIN_CROSSED);
}
/**
* @dev Gets the sample lifetime from an encoded sample
* @param sample The encoded sample as follows:
* [0 - 208[: any (208 bits)
* [208 - 216[: sample lifetime (8 bits)
* [216 - 256[: any (40 bits)
* @return lifetime The sample lifetime
*/
function getSampleLifetime(bytes32 sample) internal pure returns (uint8 lifetime) {
return sample.decodeUint8(OFFSET_SAMPLE_LIFETIME);
}
/**
* @dev Gets the sample creation timestamp from an encoded sample
* @param sample The encoded sample as follows:
* [0 - 216[: any (216 bits)
* [216 - 256[: sample creation timestamp (40 bits)
* @return creation The sample creation timestamp
*/
function getSampleCreation(bytes32 sample) internal pure returns (uint40 creation) {
return sample.decodeUint40(OFFSET_SAMPLE_CREATION);
}
/**
* @dev Gets the sample last update timestamp from an encoded sample
* @param sample The encoded sample as follows:
* [0 - 216[: any (216 bits)
* [216 - 256[: sample creation timestamp (40 bits)
* @return lastUpdate The sample last update timestamp
*/
function getSampleLastUpdate(bytes32 sample) internal pure returns (uint40 lastUpdate) {
lastUpdate = getSampleCreation(sample) + getSampleLifetime(sample);
}
/**
* @dev Gets the weighted average of two samples and their respective weights
* @param sample1 The first encoded sample
* @param sample2 The second encoded sample
* @param weight1 The weight of the first sample
* @param weight2 The weight of the second sample
* @return weightedAverageId The weighted average id
* @return weightedAverageVolatility The weighted average volatility
* @return weightedAverageBinCrossed The weighted average bin crossed
*/
function getWeightedAverage(bytes32 sample1, bytes32 sample2, uint40 weight1, uint40 weight2)
internal
pure
returns (uint64 weightedAverageId, uint64 weightedAverageVolatility, uint64 weightedAverageBinCrossed)
{
uint256 cId1 = getCumulativeId(sample1);
uint256 cVolatility1 = getCumulativeVolatility(sample1);
uint256 cBinCrossed1 = getCumulativeBinCrossed(sample1);
if (weight2 == 0) return (uint64(cId1), uint64(cVolatility1), uint64(cBinCrossed1));
uint256 cId2 = getCumulativeId(sample2);
uint256 cVolatility2 = getCumulativeVolatility(sample2);
uint256 cBinCrossed2 = getCumulativeBinCrossed(sample2);
if (weight1 == 0) return (uint64(cId2), uint64(cVolatility2), uint64(cBinCrossed2));
uint256 totalWeight = uint256(weight1) + weight2;
unchecked {
weightedAverageId = uint64((cId1 * weight1 + cId2 * weight2) / totalWeight);
weightedAverageVolatility = uint64((cVolatility1 * weight1 + cVolatility2 * weight2) / totalWeight);
weightedAverageBinCrossed = uint64((cBinCrossed1 * weight1 + cBinCrossed2 * weight2) / totalWeight);
}
}
/**
* @dev Updates a sample with the given values
* @param sample The encoded sample
* @param deltaTime The time elapsed since the last update
* @param activeId The active id
* @param volatilityAccumulator The volatility accumulator
* @param binCrossed The bin crossed
* @return cumulativeId The cumulative id
* @return cumulativeVolatility The cumulative volatility
* @return cumulativeBinCrossed The cumulative bin crossed
*/
function update(bytes32 sample, uint40 deltaTime, uint24 activeId, uint24 volatilityAccumulator, uint24 binCrossed)
internal
pure
returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed)
{
unchecked {
cumulativeId = uint64(activeId) * deltaTime;
cumulativeVolatility = uint64(volatilityAccumulator) * deltaTime;
cumulativeBinCrossed = uint64(binCrossed) * deltaTime;
}
cumulativeId += getCumulativeId(sample);
cumulativeVolatility += getCumulativeVolatility(sample);
cumulativeBinCrossed += getCumulativeBinCrossed(sample);
}
}{
"optimizer": {
"enabled": true,
"runs": 800
},
"evmVersion": "paris",
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract ILBFactory","name":"factory_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"BinHelper__CompositionFactorFlawed","type":"error"},{"inputs":[],"name":"BinHelper__LiquidityOverflow","type":"error"},{"inputs":[],"name":"BinHelper__MaxLiquidityPerBinExceeded","type":"error"},{"inputs":[],"name":"FeeHelper__FeeTooLarge","type":"error"},{"inputs":[],"name":"Hooks__CallFailed","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"LBPair__AddressZero","type":"error"},{"inputs":[],"name":"LBPair__EmptyMarketConfigs","type":"error"},{"inputs":[],"name":"LBPair__FlashLoanCallbackFailed","type":"error"},{"inputs":[],"name":"LBPair__FlashLoanInsufficientAmount","type":"error"},{"inputs":[],"name":"LBPair__InsufficientAmountIn","type":"error"},{"inputs":[],"name":"LBPair__InsufficientAmountOut","type":"error"},{"inputs":[],"name":"LBPair__InvalidHooks","type":"error"},{"inputs":[],"name":"LBPair__InvalidInput","type":"error"},{"inputs":[],"name":"LBPair__InvalidStaticFeeParameters","type":"error"},{"inputs":[],"name":"LBPair__MaxTotalFeeExceeded","type":"error"},{"inputs":[],"name":"LBPair__OnlyFactory","type":"error"},{"inputs":[],"name":"LBPair__OnlyProtocolFeeRecipient","type":"error"},{"inputs":[],"name":"LBPair__OutOfLiquidity","type":"error"},{"inputs":[],"name":"LBPair__TokenNotSupported","type":"error"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"LBPair__ZeroAmount","type":"error"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"LBPair__ZeroAmountsOut","type":"error"},{"inputs":[],"name":"LBPair__ZeroBorrowAmount","type":"error"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"LBPair__ZeroShares","type":"error"},{"inputs":[],"name":"LBToken__AddressThisOrZero","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LBToken__BurnExceedsBalance","type":"error"},{"inputs":[],"name":"LBToken__InvalidLength","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"LBToken__SelfApproval","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"LBToken__SpenderNotApproved","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LBToken__TransferExceedsBalance","type":"error"},{"inputs":[],"name":"LiquidityConfigurations__InvalidConfig","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OracleHelper__InvalidOracleId","type":"error"},{"inputs":[],"name":"OracleHelper__LookUpTimestampTooOld","type":"error"},{"inputs":[],"name":"OracleHelper__NewLengthTooSmall","type":"error"},{"inputs":[],"name":"PackedUint128Math__AddOverflow","type":"error"},{"inputs":[],"name":"PackedUint128Math__MultiplierTooLarge","type":"error"},{"inputs":[],"name":"PackedUint128Math__SubUnderflow","type":"error"},{"inputs":[],"name":"PairParametersHelper__InvalidParameter","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"SafeCast__Exceeds128Bits","type":"error"},{"inputs":[],"name":"SafeCast__Exceeds24Bits","type":"error"},{"inputs":[],"name":"SafeCast__Exceeds40Bits","type":"error"},{"inputs":[],"name":"TokenHelper__TransferFailed","type":"error"},{"inputs":[],"name":"Uint128x128Math__LogUnderflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"Uint128x128Math__PowUnderflow","type":"error"},{"inputs":[],"name":"Uint256x256Math__MulDivOverflow","type":"error"},{"inputs":[],"name":"Uint256x256Math__MulShiftOverflow","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"bytes32","name":"protocolFees","type":"bytes32"}],"name":"CollectedProtocolFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"bytes32","name":"totalFees","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"protocolFees","type":"bytes32"}],"name":"CompositionFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"bytes32[]","name":"amounts","type":"bytes32[]"}],"name":"DepositedToBins","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"contract ILBFlashLoanCallback","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint24","name":"activeId","type":"uint24"},{"indexed":false,"internalType":"bytes32","name":"amounts","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"totalFees","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"protocolFees","type":"bytes32"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint24","name":"idReference","type":"uint24"},{"indexed":false,"internalType":"uint24","name":"volatilityReference","type":"uint24"}],"name":"ForcedDecay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"bytes32","name":"hooksParameters","type":"bytes32"}],"name":"HooksParametersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint16","name":"oracleLength","type":"uint16"}],"name":"OracleLengthIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint16","name":"baseFactor","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"filterPeriod","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"decayPeriod","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"reductionFactor","type":"uint16"},{"indexed":false,"internalType":"uint24","name":"variableFeeControl","type":"uint24"},{"indexed":false,"internalType":"uint16","name":"protocolShare","type":"uint16"},{"indexed":false,"internalType":"uint24","name":"maxVolatilityAccumulator","type":"uint24"}],"name":"StaticFeeParametersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"bytes32","name":"amountsIn","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"amountsOut","type":"bytes32"},{"indexed":false,"internalType":"uint24","name":"volatilityAccumulator","type":"uint24"},{"indexed":false,"internalType":"bytes32","name":"totalFees","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"protocolFees","type":"bytes32"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"bytes32[]","name":"amounts","type":"bytes32[]"}],"name":"WithdrawnFromBins","type":"event"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"approveForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"batchBalances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"batchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsToBurn","type":"uint256[]"}],"name":"burn","outputs":[{"internalType":"bytes32[]","name":"amounts","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectProtocolFees","outputs":[{"internalType":"bytes32","name":"collectedProtocolFees","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILBFlashLoanCallback","name":"receiver","type":"address"},{"internalType":"bytes32","name":"amounts","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forceDecay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getActiveId","outputs":[{"internalType":"uint24","name":"activeId","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"getBin","outputs":[{"internalType":"uint128","name":"binReserveX","type":"uint128"},{"internalType":"uint128","name":"binReserveY","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBinStep","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getFactory","outputs":[{"internalType":"contract ILBFactory","name":"factory","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"getIdFromPrice","outputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getLBHooksParameters","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"swapForY","type":"bool"},{"internalType":"uint24","name":"id","type":"uint24"}],"name":"getNextNonEmptyBin","outputs":[{"internalType":"uint24","name":"nextId","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracleParameters","outputs":[{"internalType":"uint8","name":"sampleLifetime","type":"uint8"},{"internalType":"uint16","name":"size","type":"uint16"},{"internalType":"uint16","name":"activeSize","type":"uint16"},{"internalType":"uint40","name":"lastUpdated","type":"uint40"},{"internalType":"uint40","name":"firstTimestamp","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint40","name":"lookupTimestamp","type":"uint40"}],"name":"getOracleSampleAt","outputs":[{"internalType":"uint64","name":"cumulativeId","type":"uint64"},{"internalType":"uint64","name":"cumulativeVolatility","type":"uint64"},{"internalType":"uint64","name":"cumulativeBinCrossed","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"getPriceFromId","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getProtocolFees","outputs":[{"internalType":"uint128","name":"protocolFeeX","type":"uint128"},{"internalType":"uint128","name":"protocolFeeY","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint128","name":"reserveX","type":"uint128"},{"internalType":"uint128","name":"reserveY","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStaticFeeParameters","outputs":[{"internalType":"uint16","name":"baseFactor","type":"uint16"},{"internalType":"uint16","name":"filterPeriod","type":"uint16"},{"internalType":"uint16","name":"decayPeriod","type":"uint16"},{"internalType":"uint16","name":"reductionFactor","type":"uint16"},{"internalType":"uint24","name":"variableFeeControl","type":"uint24"},{"internalType":"uint16","name":"protocolShare","type":"uint16"},{"internalType":"uint24","name":"maxVolatilityAccumulator","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"amountOut","type":"uint128"},{"internalType":"bool","name":"swapForY","type":"bool"}],"name":"getSwapIn","outputs":[{"internalType":"uint128","name":"amountIn","type":"uint128"},{"internalType":"uint128","name":"amountOutLeft","type":"uint128"},{"internalType":"uint128","name":"fee","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"amountIn","type":"uint128"},{"internalType":"bool","name":"swapForY","type":"bool"}],"name":"getSwapOut","outputs":[{"internalType":"uint128","name":"amountInLeft","type":"uint128"},{"internalType":"uint128","name":"amountOut","type":"uint128"},{"internalType":"uint128","name":"fee","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenX","outputs":[{"internalType":"contract IERC20","name":"tokenX","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTokenY","outputs":[{"internalType":"contract IERC20","name":"tokenY","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getVariableFeeParameters","outputs":[{"internalType":"uint24","name":"volatilityAccumulator","type":"uint24"},{"internalType":"uint24","name":"volatilityReference","type":"uint24"},{"internalType":"uint24","name":"idReference","type":"uint24"},{"internalType":"uint40","name":"timeOfLastUpdate","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"newLength","type":"uint16"}],"name":"increaseOracleLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"baseFactor","type":"uint16"},{"internalType":"uint16","name":"filterPeriod","type":"uint16"},{"internalType":"uint16","name":"decayPeriod","type":"uint16"},{"internalType":"uint16","name":"reductionFactor","type":"uint16"},{"internalType":"uint24","name":"variableFeeControl","type":"uint24"},{"internalType":"uint16","name":"protocolShare","type":"uint16"},{"internalType":"uint24","name":"maxVolatilityAccumulator","type":"uint24"},{"internalType":"uint24","name":"activeId","type":"uint24"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes32[]","name":"liquidityConfigs","type":"bytes32[]"},{"internalType":"address","name":"refundTo","type":"address"}],"name":"mint","outputs":[{"internalType":"bytes32","name":"amountsReceived","type":"bytes32"},{"internalType":"bytes32","name":"amountsLeft","type":"bytes32"},{"internalType":"uint256[]","name":"liquidityMinted","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hooksParameters","type":"bytes32"},{"internalType":"bytes","name":"onHooksSetData","type":"bytes"}],"name":"setHooksParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"baseFactor","type":"uint16"},{"internalType":"uint16","name":"filterPeriod","type":"uint16"},{"internalType":"uint16","name":"decayPeriod","type":"uint16"},{"internalType":"uint16","name":"reductionFactor","type":"uint16"},{"internalType":"uint24","name":"variableFeeControl","type":"uint24"},{"internalType":"uint16","name":"protocolShare","type":"uint16"},{"internalType":"uint24","name":"maxVolatilityAccumulator","type":"uint24"}],"name":"setStaticFeeParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"swapForY","type":"bool"},{"internalType":"address","name":"to","type":"address"}],"name":"swap","outputs":[{"internalType":"bytes32","name":"amountsOut","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c06040523480156200001157600080fd5b506040516200615c3803806200615c83398101604081905262000034916200010a565b6001600160a01b03811660a052306080526200004f62000056565b506200013c565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000a75760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620001075780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6000602082840312156200011d57600080fd5b81516001600160a01b03811681146200013557600080fd5b9392505050565b60805160a051615fe56200017760003960008181610505015281816113ce015281816121da01526136a5015260006104720152615fe56000f3fe608060405234801561001057600080fd5b50600436106102765760003560e01c806388cc58e411610160578063c9939f5e116100d8578063e584b6541161008c578063e985e9c511610071578063e985e9c5146106c9578063ea3446bf146106ec578063f5e29329146106ff57600080fd5b8063e584b654146106a3578063e77366f8146106b657600080fd5b8063d8dfcea0116100bd578063d8dfcea01461067b578063da10610c14610683578063dbe65edc1461069b57600080fd5b8063c9939f5e14610660578063d3b9fbe41461067357600080fd5b8063a1af5b9a1161012f578063abcd783011610114578063abcd7830146105f0578063bd85b0391461062d578063c7bd65861461064d57600080fd5b8063a1af5b9a146105c1578063a41a01fb146105c957600080fd5b806388cc58e4146105035780638940a16a146105295780638d7024e51461056757806395d89b41146105a257600080fd5b806347973bff116101f357806355182894116101c25780636653851a116101a75780636653851a14610494578063781a8915146104a75780637ca0de30146104b157600080fd5b806355182894146104285780635c60da1b1461046d57600080fd5b806347973bff146103cf5780634c7cffbd146103e25780634e1273f4146103f557806353c059a01461041557600080fd5b80630abe96881161024a57806317fad7fc1161022f57806317fad7fc146103855780631b057f6d1461039a578063383d15c5146103ad57600080fd5b80630abe96881461033457806317f11ecc1461036a57600080fd5b8062fdd58e1461027b57806305e8746d146102a157806306fdde03146102ca5780630902f1ac1461030c575b600080fd5b61028e61028936600461556c565b610712565b6040519081526020015b60405180910390f35b3660011981013560f01c90033560601c5b6040516001600160a01b039091168152602001610298565b60408051808201909152601481527f4c697175696469747920426f6f6b20546f6b656e00000000000000000000000060208201525b60405161029891906155bc565b610314610740565b604080516001600160801b03938416815292909116602083015201610298565b610314610342366004615602565b62ffffff166000908152600660205260409020546001600160801b0381169160809190911c90565b61037261077a565b60405161ffff9091168152602001610298565b610398610393366004615662565b61079b565b005b6103986103a8366004615739565b6107f6565b6103c06103bb366004615785565b610915565b60405161029893929190615828565b6103986103dd366004615859565b610b9b565b61028e6103f0366004615602565b610cf9565b6104086104033660046158f0565b610d29565b604051610298919061595c565b61028e61042336600461597f565b610dff565b610430611138565b6040805160ff909616865261ffff9485166020870152939092169284019290925264ffffffffff918216606084015216608082015260a001610298565b6102b27f000000000000000000000000000000000000000000000000000000000000000081565b6103986104a23660046159b6565b6111e2565b620100095461028e565b6104b961121b565b6040805161ffff9889168152968816602088015294871694860194909452918516606085015262ffffff9081166080850152931660a08301529190911660c082015260e001610298565b7f00000000000000000000000000000000000000000000000000000000000000006102b2565b61053c610537366004615a3c565b611283565b6040805167ffffffffffffffff94851681529284166020840152921691810191909152606001610298565b61056f611382565b6040805162ffffff95861681529385166020850152919093169082015264ffffffffff9091166060820152608001610298565b60408051808201909152600381526213109560ea1b60208201526102ff565b61028e6113c2565b6105dc6105d7366004615a63565b61157a565b60405162ffffff9091168152602001610298565b6106036105fe366004615a8d565b61158d565b604080516001600160801b0394851681529284166020840152921691810191909152606001610298565b61028e61063b366004615ac5565b60009081526001602052604090205490565b61039861065b366004615ade565b611732565b61040861066e366004615662565b6117c8565b610398611af2565b610314611b86565b3660011981013560f01c90036014013560601c6102b2565b6105dc611ba1565b6103986106b1366004615af9565b611bae565b6106036106c4366004615a8d565b611bbd565b6106dc6106d7366004615b17565b611d03565b6040519015158152602001610298565b6103986106fa366004615b35565b611d0f565b6105dc61070d366004615ac5565b611fed565b6001600160a01b0382166000908152602081815260408083208484529091529020545b92915050565b905090565b60008061077161075d60055460045461201890919063ffffffff16565b6001600160801b0381169160809190911c90565b90939092509050565b600061073b60003660011981013560f090811c90910360280135901c905090565b6107a3612052565b62010009546107b8813389898989898961208a565b6107c687878787878761212a565b6107dd6001600080516020615f9083398151915255565b6107ed8133898989898989612188565b50505050505050565b6107fe612052565b6108066121cf565b62010009839055604080518481529051849133917f4d24d81a1a6e09c37497747b4c9d67d858ce80438b54df0fa361a24dc742d9349181900360200190a26001600160a01b038116158015906108cf5750306001600160a01b0316816001600160a01b0316635f9c01b16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561089f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c39190615b85565b6001600160a01b031614155b156108ed57604051631b5de6eb60e01b815260040160405180910390fd5b6108f8848484612218565b506109106001600080516020615f9083398151915255565b505050565b6000806060866109248161223f565b61092c612052565b600086900361094e5760405163296db0d960e01b815260040160405180910390fd5b62010009546040805160608101909152600090808967ffffffffffffffff81111561097b5761097b615ba2565b6040519080825280602002602001820160405280156109a4578160200160208202803683370190505b5081526020018967ffffffffffffffff8111156109c3576109c3615ba2565b6040519080825280602002602001820160405280156109ec578160200160208202803683370190505b5081526020018967ffffffffffffffff811115610a0b57610a0b615ba2565b604051908082528060200260200182016040528015610a34578160200160208202803683370190505b509052600454909150610a60813660011981013560f01c90038035606090811c9160140135901c61227b565b9650610a7083338d8d8d8c6122bb565b610a7d8a8a898e86612305565b9550610a93610a8c8888612018565b829061243a565b600455604080830151835191519096506001600160a01b038d169160009133917f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb91610ae191908b90615bb8565b60405180910390a4815160208301516040516001600160a01b038e169233927f87f1f9dcf5e8089a3e00811b6a008d8f30293a3da878cb1fe8c90ca376402f8a92610b2d929190615bb8565b60405180910390a38515610b5f57610b5f863660011981013560f01c90038035606090811c9160140135901c8b612474565b610b766001600080516020615f9083398151915255565b610b8d83338d8d8d610b888d8d612018565b6124d2565b505050509450945094915050565b610ba36121cf565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610bee5750825b905060008267ffffffffffffffff166001148015610c0b5750303b155b905081158015610c19575080155b15610c375760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610c6b57845468ff00000000000000001916680100000000000000001785555b610c73612514565b610c9f610c93610c8e8860035461252490919063ffffffff16565b612573565b8e8e8e8e8e8e8e6125c7565b8315610cea57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b6000610735610d1d60003660011981013560f090811c90910360280135901c905090565b62ffffff84169061274e565b60608382610d378282612785565b8567ffffffffffffffff811115610d5057610d50615ba2565b604051908082528060200260200182016040528015610d79578160200160208202803683370190505b50925060005b86811015610df457610dcf888883818110610d9c57610d9c615bdd565b9050602002016020810190610db19190615bf3565b878784818110610dc357610dc3615bdd565b90506020020135610712565b848281518110610de157610de1615bdd565b6020908102919091010152600101610d7f565b505050949350505050565b6000610e09612052565b6201000954600454600554600086610e3d57610e383660011981013560f01c90036014013560601c84906127a5565b610e57565b610e573660011981013560f01c90033560601c84906127da565b90506000819003610e7b57604051637df801c760e01b815260040160405180910390fd5b86610e898533898486612809565b610e93848361243a565b6003549094506000610eba60003660011981013560f090811c90910360280135901c905090565b90506000610ec78361286d565b9050610ed38342612879565b92505b62ffffff8116600090815260066020526040902054610ef68186156128ef565b61101a57610f04848361291e565b935060008080610f188488888b898e6129a6565b91945092509050821561101657610f2f8984612018565b9850610f3b8d8361243a565b9c506000610f56610f4b89612bcc565b839061ffff16612bdc565b90508015610f7757610f688b8261243a565b9a50610f748482612018565b93505b610f8b83610f85878761243a565b90612018565b62ffffff87166000908152600660205260409020556001600160a01b038f16337fad7d6f97abf51ce18e17a38f4d70e975be9c0708474987bb3e26ad21bd93ca70888787610fd88e612c52565b6040805162ffffff9586168152602081019490945283019190915290911660608201526080810186905260a0810185905260c00160405180910390a3505b5050505b60008690036110295750611073565b60006110358684612c63565b905062ffffff8116158061104d575062ffffff818116145b1561106b57604051631a6d7fb160e31b815260040160405180910390fd5b915050610ed6565b60008990036110955760405163439df85d60e11b815260040160405180910390fd5b61109f878a612018565b60045560058690556110b3600a8483612c85565b92506110bf8382612524565b60035583156110eb576110e63660011981013560f01c90036014013560601c8a908c612da0565b611106565b6111063660011981013560f01c90033560601c8a908c612de0565b61111d6001600080516020615f9083398151915255565b61112a88338c878d612df1565b505050505050505092915050565b6003546078906000908190819081908161115182612e4e565b905061ffff8116156111d957600061116a600a83612e5e565b9650905061117781612ebc565b965061118281612ec8565b94508464ffffffffff1660000361119857600095505b61ffff8616156111d7576111c98661ffff168361ffff16816111bc576111bc615c10565b600a916001910601612ee9565b90506111d481612ec8565b93505b505b50509091929394565b6111ea612052565b6111f26121cf565b611204600354888888888888886125c7565b6107ed6001600080516020615f9083398151915255565b600080600080600080600080600354905061123581612ebc565b975061124081612f19565b965061124b81612f29565b955061125681612f39565b945061126181612f49565b935061126c81612bcc565b925061127781612f5a565b91505090919293949596565b6000806000806003549050600061129982612e4e565b905061ffff811615806112b25750428664ffffffffff16115b156112c9576000806000945094509450505061137b565b60006112d7600a8389612f6b565b91985096509450905064ffffffffff80881690821610156113775761130d6112fe8461286d565b849064ffffffffff8a1661308e565b9250600061131b8289615c3c565b90508064ffffffffff1661132e8561286d565b62ffffff1661133d9190615c5a565b6113479088615c86565b96508064ffffffffff1661135a85612c52565b62ffffff166113699190615c5a565b6113739087615c86565b9550505b5050505b9193909250565b6000806000806000600354905061139881612c52565b94506113a3816130a6565b93506113ae816130b7565b92506113b9816130c8565b91505090919293565b60006113cc612052565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634ccb20c06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561142a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144e9190615b85565b6001600160a01b0316336001600160a01b03161461147f57604051630f8937c560e21b815260040160405180910390fd5b6005546001600160801b038116608082901c60006114da826114a25760006114a5565b60015b60ff166000856001600160801b0316116114c05760006114c3565b60015b60ff169060801b6001600160801b03919091161790565b90506114e68482612018565b9450841561155c5760058190556004546115009086612018565b60045560405185815233907f3f41a5ddc53701cc7db577ade4f1fca9838a8ec0b5ea50b9f0f5d17bc4554e329060200160405180910390a261155c853660011981013560f01c90038035606090811c9160140135901c33612474565b505050506115776001600080516020615f9083398151915255565b90565b60006115868383612c63565b9392505050565b60035460009083908290816115b760003660011981013560f090811c90910360280135901c905090565b905060006115c48361286d565b90506115d08342612879565b92505b62ffffff81166000908152600660205260408120546115f39089156130db565b90506001600160801b038116156116d957600061161562ffffff84168561274e565b90506000876001600160801b0316836001600160801b031611611638578261163a565b875b9050611646868561291e565b955060008a611669576116646001600160801b0383168460806130fb565b61167e565b61167e6001600160801b038316608085613131565b9050600061168c8888613167565b905060006116a36001600160801b03841683613186565b90506116af8184615ca7565b6116b9908d615ca7565b9b506116c5848c615cc7565b9a506116d1818b615ca7565b995050505050505b856001600160801b03166000036116f05750611728565b60006116fc8984612c63565b905062ffffff81161580611714575062ffffff818116145b15611720575050611728565b9150506115d3565b5050509250925092565b61173a612052565b600354600061174882612e4e565b90508061ffff166000036117675750600161176382826131cf565b6003555b611773600a828561321e565b60405161ffff8416815233907fc975541e72d695746a43ba65745d79963a23082637c8f4609354d9bcf70194d69060200160405180910390a250506117c56001600080516020615f9083398151915255565b50565b606086336117d68282613307565b61180b5760405163548f773d60e01b81526001600160a01b038084166004830152821660248201526044015b60405180910390fd5b611813612052565b8515806118205750858414155b1561183e57604051633ab7870760e01b815260040160405180910390fd5b620100095461185381338c8c8c8c8c8c613353565b898767ffffffffffffffff81111561186d5761186d615ba2565b604051908082528060200260200182016040528015611896578160200160208202803683370190505b5094506000805b898110156119ea5760006118c88c8c848181106118bc576118bc615bdd565b90506020020135613399565b905060008a8a848181106118de576118de615bdd565b9050602002013590508060000361190f57604051636996a92560e01b815262ffffff83166004820152602401611802565b62ffffff8216600081815260066020908152604080832054600190925290912054909161193e908890856133c4565b600061194b838584613445565b905060008190036119765760405163fd44792960e01b815262ffffff86166004820152602401611802565b6119808382612018565b9250838203611996576119946007866134bd565b505b62ffffff851660009081526006602052604090208390558b5181908d90889081106119c3576119c3615bdd565b60209081029190910101526119d8878261243a565b9650856001019550505050505061189d565b506004546119f89082612018565b6004556040516000906001600160a01b0384169033907f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb90611a41908f908f908f908f90615d32565b60405180910390a48a6001600160a01b0316336001600160a01b03167fa32e146844d6144a22e94c586715a1317d58a8aa3581ec33d040113ddcb243508c8c8a604051611a9093929190615d59565b60405180910390a3611abc813660011981013560f01c90038035606090811c9160140135901c8e612474565b611ad36001600080516020615f9083398151915255565b611ae38333848e8e8e8e8e61355f565b50505050509695505050505050565b611afa612052565b611b026121cf565b600354611b16611b1182612573565b6135a5565b600355337f282afaeeae84c1d85ad1424a3aa2ddbedaeefca3b1e53d889d15265fe44db7fc611b44836130b7565b611b4d846130a6565b6040805162ffffff93841681529290911660208301520160405180910390a250611b846001600080516020615f9083398151915255565b565b60055460009081906001600160801b0381169060801c610771565b600061073b60035461286d565b611bb93383836135da565b5050565b6000808080611bd56001600160801b0387168661368f565b6003549091506000611bfc60003660011981013560f090811c90910360280135901c905090565b90506000611c098361286d565b9050611c158342612879565b92505b62ffffff8116600090815260066020526040902054611c38818a156128ef565b611ca557611c46848361291e565b935060008080611c5a8488888f898d6129a6565b919450925090508215611ca157611c718884612018565b9750611c7e828d156130db565b611c88908b615ca7565b9950611c94818d6130db565b611c9e908a615ca7565b98505b5050505b6000859003611cb45750611cec565b6000611cc08a84612c63565b905062ffffff81161580611cd8575062ffffff818116145b15611ce4575050611cec565b915050611c18565b611cf684896130db565b9650505050509250925092565b60006115868383613307565b611d17612052565b6000839003611d395760405163727ab02160e11b815260040160405180910390fd5b62010009546004546000611d4c866136a0565b9050611d5a833389896137a8565b611d7e863660011981013560f01c90038035606090811c9160140135901c8a612474565b6000806001600160a01b038916631faa6b8760e01b333660011981013560f01c90033560601c3660011981013560f01c90036014013560601c8c888d8d604051602401611dd19796959493929190615da8565b60408051601f198184030181529181526020820180516001600160e01b03167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611e279190615de9565b6000604051808303816000865af19150503d8060008114611e64576040519150601f19603f3d011682016040523d82523d6000602084013e611e69565b606091505b5091509150811580611e7d57508051602014155b80611ebb57507fab5c473bce5960a8292e9c8db82f9272504caab4e9ef09553fc112f82b62a3c281806020019051810190611eb89190615e05565b14155b15611ed957604051634a57011360e01b815260040160405180910390fd5b6000611efe813660011981013560f01c90038035606090811c9160140135901c61227b565b9050611f14611f0d868661243a565b82906137fd565b15611f3257604051630e3c717160e11b815260040160405180910390fd5b6000611f3e8287612018565b6004839055600554909150611f53908261243a565b6005556003546001600160a01b038c169033907fd126bd9d94daca8e55ffd8283fac05394aec8326c6b1639e1e8a445fbe8bbc7d90611f919061286d565b6040805162ffffff9092168252602082018f905281018590526060810185905260800160405180910390a3611fd36001600080516020615f9083398151915255565b611fe087338d8885613841565b5050505050505050505050565b600061073561201160003660011981013560f090811c90910360280135901c905090565b839061389d565b8082038281118061203457506001600160801b03808416908216115b156107355760405163e599af5560e01b815260040160405180910390fd5b600080516020615f9083398151915280546001190161208457604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b750100000000000000000000000000000000000000000088161561212057612120886379c8ccf760e01b898989898989896040516024016120d19796959493929190615e1e565b60408051601f198184030181529190526020810180516001600160e01b03167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526138e2565b5050505050505050565b85336121368282613307565b6121665760405163548f773d60e01b81526001600160a01b03808416600483015282166024820152604401611802565b612120888888888888613946565b6001600080516020615f9083398151915255565b750200000000000000000000000000000000000000000088161561212057612120886367ddb27860e01b898989898989896040516024016120d19796959493929190615e1e565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611b8457604051632486085360e01b815260040160405180910390fd5b82156109105761091083634e430b5a60e01b8585856040516024016120d193929190615e74565b6001600160a01b038116158061225d57506001600160a01b03811630145b156117c5576040516345c210e760e11b815260040160405180910390fd5b60006122b384610f8561228d85613aa9565b61229687613aa9565b6001600160801b03169060801b6001600160801b03919091161790565b949350505050565b741000000000000000000000000000000000000000008616156122fd576122fd86630d4abdb360e01b87878787876040516024016120d1959493929190615e8e565b505050505050565b60008061232760003660011981013560f090811c90910360280135901c905090565b60035490915060006123388261286d565b905086935060005b8881101561242d576000806123768a8d8d8681811061236157612361615bdd565b90506020020135613b1890919063ffffffff16565b91509150600080600061238c898886888c613b61565b9194509250905061239d8a83612018565b99508362ffffff168b6000015187815181106123bb576123bb615bdd565b602002602001018181525050808b6020015187815181106123de576123de615bdd565b602002602001018181525050828b60400151878151811061240157612401615bdd565b60200260200101818152505061241d8c8562ffffff1685613d6f565b8560010195505050505050612340565b5050505095945050505050565b8181018281108061245657506001600160801b03808416908216105b15610735576040516316692de960e01b815260040160405180910390fd5b6001600160801b038416608085901c81156124a6576124a66001600160a01b038616846001600160801b038516613dbc565b6001600160801b038116156122fd576122fd6001600160a01b038516846001600160801b038416613dbc565b742000000000000000000000000000000000000000008616156122fd576122fd86637a51a4c660e01b87878787876040516024016120d1959493929190615e8e565b61251c613e0d565b611b84613e5b565b60007cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83167fffffff000000000000000000000000000000000000000000000000000000000060e884901b1617611586565b60008061257f8361286d565b90507fffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffff831675ffffff00000000000000000000000000000000000000609883901b1617611586565b61ffff87161580156125db575061ffff8616155b80156125e9575061ffff8516155b80156125f7575061ffff8416155b8015612606575062ffffff8316155b8015612614575061ffff8216155b8015612623575062ffffff8116155b15612641576040516331375e0360e11b815260040160405180910390fd5b6126518888888888888888613e63565b9750600061267460003660011981013560f090811c90910360280135901c905090565b905060006126828a84613f7c565b905060006126908284613fda565b61269a838561401a565b6126a49190615ecd565b905067016345785d8a00008111156126cf576040516372db9c0b60e11b815260040160405180910390fd5b50505060038890556040805161ffff8981168252888116602083015287811682840152868116606083015262ffffff868116608084015290851660a0830152831660c0820152905133917fd09e5ddc721ff14c5c1e66a305cbba1fd70b82c5232bc391aad6f55e62e4b046919081900360e00190a25050505050505050565b600061271061ffff60801b608084901b1604600160801b0162ffffff8416627fffff190161277c828261403e565b95945050505050565b808214611bb9576040516340311ffd60e11b815260040160405180910390fd5b6000806127b28460801c90565b90506122b3816127c185613aa9565b6127cb9190615cc7565b6001600160801b031660801b90565b60006001600160801b0383166122b3816127f385613aa9565b6127fd9190615cc7565b6001600160801b031690565b74010000000000000000000000000000000000000000851615612866576040516001600160a01b0380861660248301528416604482015282151560648201526084810182905261286690869063feee373560e01b9060a4016120d1565b5050505050565b600060e882901c610735565b600080612885846130c8565b6128969064ffffffffff1684615ee0565b90506128a184612f19565b61ffff1681106128e5576128b484612573565b93506128bf84612f29565b61ffff1681106128d9576128d484600061429e565b6128e2565b6128e2846135a5565b93505b6122b3848461430c565b60008161290f576129008360801c90565b6001600160801b031615611586565b50506001600160801b03161590565b60008061292a846130b7565b62ffffff169050600080828562ffffff161161294d578462ffffff168303612956565b828562ffffff16035b91506127108202612966876130a6565b62ffffff16019050600061297987612f5a565b62ffffff16905080821161298d578161298f565b805b915061299b8783613f7c565b979650505050505050565b60008080806129ba62ffffff87168961274e565b905060006129c98b89156130db565b90506000886129f4576129ef6129ea6001600160801b0384168560806130fb565b614365565b612a0c565b612a0c6129ea6001600160801b038416608086613131565b90506000612a1a8c8c613167565b90506000612a316001600160801b03841683613186565b9050612a3d8184615ca7565b92506000612a4b8a8d6130db565b9050600080856001600160801b0316836001600160801b031610612a76575084915082905085612aec565b612a896001600160801b0384168661438f565b91506000612a978385615cc7565b6001600160801b031690508e612abb57612ab66129ea8260808c6143c6565b612aca565b612aca6129ea828b60806143f0565b9150876001600160801b0316826001600160801b03161115612aea578791505b505b8d612b2657612b04836001600160801b031660801b90565b6001600160801b038216612b21846001600160801b031660801b90565b612b4e565b6001600160801b038316612b43826001600160801b031660801b90565b6001600160801b0384165b809b50819c50829d50505050505050505050507d09745258e83de0d0f4e400fce79aca44773dd596d31a6d336ca2a775b611612ba0612b9a85610f85888f61243a90919063ffffffff16565b8361445e565b1115612bbf5760405163a2d7652760e01b815260040160405180910390fd5b5096509650969350505050565b600061073582604e1c613fff1690565b6000816001600160801b0316600003612bf757506000610735565b6127106001600160801b038316811015612c2457604051631514a94960e21b815260040160405180910390fd5b6001600160801b03848116840282900490608086811c86028490049183169082901b175b9695505050505050565b60006107358260701c620fffff1690565b600082612c7a57612c7560078361447a565b611586565b6115866007836145c6565b600080612c9184612e4e565b90508061ffff16600003612ca85783915050611586565b6000612cb48683612ee9565b90506000612cc1826146f6565b90506000612cce83614702565b612cdb9060ff1683615ef3565b90508064ffffffffff16612cee42614711565b64ffffffffff161115612d94576000806000612d338464ffffffffff164203612d168c61286d565b612d1f8d612c52565b612d298e8e614739565b8a93929190614769565b9250925092506000612d4487612ebc565b905064ffffffffff861642036078811115612d74575096879006600101964295506000612d718c8a6131cf565b9b505b612d8282868686858c6147ce565b97505050505050612d94888585614872565b50949695505050505050565b6000612dac8460801c90565b90506001600160801b03811615612dda57612dda6001600160a01b038416836001600160801b038416613dbc565b50505050565b60006001600160801b038416612dac565b74020000000000000000000000000000000000000000851615612866576040516001600160a01b038086166024830152841660448201528215156064820152608481018290526128669086906353e8079b60e01b9060a4016120d1565b60006107358260d81c61ffff1690565b600080612e6b8484612ee9565b9150612e7682612ebc565b90508061ffff168361ffff1614612eb557612e99612e948583612ee9565b612ebc565b90508061ffff168361ffff1611612eb05780612eb2565b825b90505b9250929050565b600061ffff8216610735565b6000612ed382614702565b60ff16612edf836146f6565b6107359190615ef3565b600081612ef58161489f565b8361ffff60001985018116908110612f0f57612f0f615bdd565b0154949350505050565b60006107358260101c610fff1690565b600061073582601c1c610fff1690565b60006107358260281c613fff1690565b60006107358260361c62ffffff1690565b600061073582605c1c620fffff1690565b600080600080600080612f7e8989612e5e565b909250905064ffffffffff8716612fb58a612f99848c615f11565b61ffff1661ffff8110612fae57612fae615bdd565b0154612ec8565b64ffffffffff161115612fdb57604051631c4e7f9560e21b815260040160405180910390fd5b612fe482612ec8565b95508664ffffffffff168664ffffffffff16116130265785613005836148c4565b61300e846148da565b613017856148f0565b95509550955095505050613085565b8695506000806130388b8b8b86614906565b9150915060008961304883612ec8565b6130529190615c3c565b9050600061305f84612ec8565b613069908c615c3c565b905061307784848484614a58565b919a50985096505050505050505b93509350935093565b600061309a8483612879565b93506122b3848461291e565b60006107358260841c620fffff1690565b60006107358260981c62ffffff1690565b60006107358260b01c64ffffffffff1690565b6000816130ec57612c758360801c90565b6001600160801b038316611586565b60006131088484846143f0565b9050600160ff83161b8061311e5761311e615c10565b83850915611586576122b3600182615ecd565b600061313e8484846143c6565b9050818061314e5761314e615c10565b600160ff85161b850915611586576122b3600182615ecd565b60006115866131768484613fda565b613180858561401a565b01614365565b60008161319281614bbc565b6001600160801b03808416670de0b6b3a764000081900391829160001991881602820101816131c3576131c3615c10565b049250505b5092915050565b60007fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff83167cffff00000000000000000000000000000000000000000000000000000060d884901b1617611586565b600061322a8484612ee9565b9050600061323782612ebc565b90508261ffff168161ffff161061326157604051632c57574160e21b815260040160405180910390fd5b60008461ffff168261ffff16146132925761ffff82161561328b576132868683612ee9565b613294565b6000613294565b825b905060006132a182612ebc565b61ffff169050808661ffff16116132b857806132be565b8561ffff165b905061ffff83165b8561ffff168110156132f05781888261ffff81106132e6576132e6615bdd565b01556001016132c6565b506107ed878761ffff868116881890891617614872565b6000816001600160a01b0316836001600160a01b031614806115865750506001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b74400000000000000000000000000000000000000000881615612120576121208863ea9db9d360e01b898989898989896040516024016120d19796959493929190615e1e565b8062ffffff811681146133bf57604051639b63641560e01b815260040160405180910390fd5b919050565b6001600160a01b03831660009081526020818152604080832085845291829052909120548281101561342157604051625b387760e31b81526001600160a01b03861660048201526024810185905260448101849052606401611802565b60009384526001602090815260408086208054869003905592905292209103905550565b60006001600160801b038416608085901c82808315613478576134756129ea886001600160801b03871689614bee565b91505b6001600160801b038316156134a15761349e6129ea886001600160801b03861689614bee565b90505b608081901b6001600160801b0383161798975050505050505050565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b19811680821461355357600083815260028701602052604081208290558190036135475750600882901c6000818152600187810160205260408220805460ff87169290921b19909116908190559190829003613545578654600160ff83161b191687555b505b60019350505050610735565b50600095945050505050565b74800000000000000000000000000000000000000000881615612120576121208863fdc2725760e01b898989898989896040516024016120d19796959493929190615e1e565b6000806135b183612c52565b62ffffff16905060006135c384612f39565b61ffff1690506127108282020461277c858261429e565b826135e48161223f565b826001600160a01b0316846001600160a01b0316036136215760405163782ee70760e01b81526001600160a01b0385166004820152602401611802565b6001600160a01b03848116600081815260026020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b6000816130ec57612c758360801b90565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fd90c2be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613701573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137259190615e05565b90506001600160801b0383811690608085901c90670de0b6b3a763ffff9061375e90670de0b6b3a7640000908616850283015b04614365565b9250613790670de0b6b3a764000082866001600160801b0316856001600160801b031602018161375857613758615c10565b915050608081901b6001600160801b0383161761277c565b74040000000000000000000000000000000000000000841615612dda576040516001600160a01b0380851660248301528316604482015260648101829052612dda90859063505d903360e01b906084016120d1565b60006001600160801b0383811690608085811c9185169085901c8184108061299b5750806001600160801b0316836001600160801b03161094505050505092915050565b74080000000000000000000000000000000000000000851615612866576040516001600160a01b0380861660248301528416604482015260648101839052608481018290526128669086906360bed5f360e01b9060a4016120d1565b600061271061ffff60801b608084901b1604600160801b01816138bf82614c0e565b6138c886614c0e565b6138d29190615f32565b905061277c816280000001613399565b600080839050602083015160e01c602060008551602087016000865af192503d151583151615613916573d6000803e3d6000fd5b8060005160e01c14601f3d1116831692505081612dda57604051636c93cb9b60e01b815260040160405180910390fd5b82816139528282612785565b8661395c8161223f565b6001600160a01b03808a16600090815260208190526040808220928b1682528120905b88811015613a405760008a8a8381811061399b5761399b615bdd565b90506020020135905060008989848181106139b8576139b8615bdd565b600085815260208981526040909120549102929092013592505081811015613a15578e83836040516358b4690f60e01b8152600401611802939291906001600160a01b039390931683526020830191909152604082015260600190565b600092835260208681526040808520928490039092558590529091208054909101905560010161397f565b50896001600160a01b03168b6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8c8c8c8c604051613a949493929190615d32565b60405180910390a45050505050505050505050565b6040516370a0823160e01b8152306004820152600090610735906001600160a01b038416906370a0823190602401602060405180830381865afa158015613af4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ea9190615e05565b6000806000806000613b2987614d1b565b670de0b6b3a764000060808a811c90930281900490921b6001600160801b03998a169093029190910490971617979596505050505050565b62ffffff83166000818152600660205260408120549091829182918290613b88908b61274e565b90506000613ba78962ffffff1660009081526001602052604090205490565b9050613bb583898484614dbe565b909650945084935062ffffff808b16908a1603613ced57613bd7878a4261308e565b96506000613be984898e89868c614f44565b90508015613ce7576000613c0784613c018985612018565b9061445e565b90506000613c22613c178b612bcc565b849061ffff16612bdc565b90508015613c4857613c348782612018565b600554909750613c44908261243a565b6005555b6000613c6286613c01613c5b8786612018565b8a9061243a565b9050613c6f838683614bee565b9950613c7d600a8c8f612c85565b9a508a600381905550336001600160a01b03167f3f0b46725027bb418b2005f4683538eccdbcdf1de2b8649a29dbd9c507d16ff48e8685604051613cdb9392919062ffffff9390931683526020830191909152604082015260600190565b60405180910390a25050505b50613cf8565b613cf8858b8b615039565b851580613d03575083155b15613d2857604051634c98d35760e11b815262ffffff8a166004820152602401611802565b80600003613d3d57613d3b60078a61509f565b505b613d47838561243a565b62ffffff90991660009081526006602052604090209890985550929891975095509350505050565b60008281526001602052604081208054839290613d8d908490615ecd565b90915550506001600160a01b039092166000908152602081815260408083209383529290522080549091019055565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612dda848261512c565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16611b8457604051631afcd79f60e31b815260040160405180910390fd5b612174613e0d565b60008561ffff168761ffff161180613e805750610fff8661ffff16115b80613e9057506127108561ffff16115b80613ea057506109c48361ffff16115b80613eb25750620fffff8262ffffff16115b15613ed057604051631c07203f60e01b815260040160405180910390fd5b605c82901b6dfffff00000000000000000000000166dffffffffffffffffffffffffffff1991821661ffff8a1690811760108a901b630fff000016908117601c8a901b64fff00000001690811760288a901b663fff00000000001690811760368a901b693fffffc000000000000016908117604e8a901b6b0fffc000000000000000000016908117871797949095179092171717909117909117908a16175b9998505050505050505050565b6000620fffff8262ffffff161115613fa757604051631c07203f60e01b815260040160405180910390fd5b700fffff0000000000000000000000000000198316700fffff0000000000000000000000000000607084901b1617611586565b600080613fe684612f49565b62ffffff16905080156131c85760008361ffff1661400386612c52565b62ffffff16029050606460638280028402016131c3565b60008161ffff1661402a84612ebc565b61ffff16026402540be40002905092915050565b60008060008360000361405a5750600160801b91506107359050565b5082600081121561406c579015906000035b6210000081101561425c57600160801b9250846001600160801b0381111561409657911591600019045b60018216156140a75792830260801c925b800260801c60028216156140bd5792830260801c925b800260801c60048216156140d35792830260801c925b800260801c60088216156140e95792830260801c925b800260801c60108216156140ff5792830260801c925b800260801c60208216156141155792830260801c925b800260801c604082161561412b5792830260801c925b8002608090811c908216156141425792830260801c925b800260801c6101008216156141595792830260801c925b800260801c6102008216156141705792830260801c925b800260801c6104008216156141875792830260801c925b800260801c61080082161561419e5792830260801c925b800260801c6110008216156141b55792830260801c925b800260801c6120008216156141cc5792830260801c925b800260801c6140008216156141e35792830260801c925b800260801c6180008216156141fa5792830260801c925b800260801c620100008216156142125792830260801c925b800260801c6202000082161561422a5792830260801c925b800260801c620400008216156142425792830260801c925b800260801c6208000082161561425a5792830260801c925b505b8260000361428757604051631dba598d60e11b81526004810186905260248101859052604401611802565b81614292578261277c565b61277c83600019615f60565b6000620fffff8262ffffff1611156142c957604051631c07203f60e01b815260040160405180910390fd5b7fffffffffffffffffffffffffff00000fffffffffffffffffffffffffffffffff831672fffff000000000000000000000000000000000608484901b1617611586565b60008061431883614711565b90507fffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffffff84167affffffffff0000000000000000000000000000000000000000000060b083901b16176122b3565b806001600160801b03811681146133bf57604051632f45db3d60e21b815260040160405180910390fd5b60008161439b81614bbc565b670de0b6b3a7640000670de0b6b3a763ffff6001600160801b03868116908616020104949350505050565b600060ff831684811b9061ffff6101008290031686901c90612c489087906001901b86858561519c565b60008060006143ff8686615248565b9150915081600014614415578360ff1682901c92505b801561445557600160ff85161b811061444157604051638e471a8960e01b815260040160405180910390fd5b8360ff166101000361ffff1681901b830192505b50509392505050565b60006001600160801b038316608084901c61277c828286615267565b60008061ffff600884901c1660ff8085169081146144cf57600082815260028701602052604081205493506144af84836152d2565b905060001981146144cd5760089290921b9091179250610735915050565b505b5060ff80821690600883901c90821461454957600081815260018801602052604081205494506144ff85846152d2565b9050600019811461454757600882901b8117600081815260028a0160205260409020549550935061452f856152e1565b60ff1660088560001c901b1795505050505050610735565b505b60ff818116925082146145b95786549350600061456685846152d2565b905060001981146145b75760008181526001890160205260409020549450905080614590856152e1565b60ff16600883901b17600081815260028a0160205260409020549550935061452f856152e1565b505b5060009695505050505050565b60008061ffff600884901c1660ff8416801561461957600082815260028701602052604081205493506145f9848361537d565b905060001981146146175760089290921b9091179250610735915050565b505b5060ff8116600882901c81156146785760008181526001880160205260408120549450614646858461537d565b9050600019811461467657600882901b8117600081815260028a0160205260409020549550935061452f8561538d565b505b60ff8116915081156146e757865493506000614694858461537d565b905060001981146146e557600081815260018901602052604090205494509050806146be8561538d565b60ff16600883901b17600081815260028a0160205260409020549550935061452f8561538d565b505b5062ffffff9695505050505050565b600060d882901c610735565b60006107358260d01c60ff1690565b8064ffffffffff811681146133bf576040516350dbfc8760e11b815260040160405180910390fd5b6000806147458461286d565b90508062ffffff168362ffffff1611614760578281036122b3565b90910392915050565b64ffffffffff841662ffffff84811682029184821681029184160261478d886148c4565b6147979084615c86565b92506147a2886148da565b6147ac9083615c86565b91506147b7886148f0565b6147c19082615c86565b9050955095509592505050565b60d082901b7aff000000000000000000000000000000000000000000000000000016609084901b79ffffffffffffffff00000000000000000000000000000000000016605086901b71ffffffffffffffff0000000000000000000016601088901b69ffffffffffffffff00001661ffff8a161717171760d882901b7fffffffffff00000000000000000000000000000000000000000000000000000016811761299b565b8161487c8161489f565b818461ffff6000198601811690811061489757614897615bdd565b015550505050565b8061ffff166000036117c55760405163944e113960e01b815260040160405180910390fd5b60006107358260101c67ffffffffffffffff1690565b60006107358260501c67ffffffffffffffff1690565b60006107358260901c67ffffffffffffffff1690565b6000808080614916600186615f74565b61ffff16905060008060008961ffff1690505b8385116149d4576000600161493e8688615ecd565b901c9050888183089a508b61ffff808d1690811061495e5761495e615bdd565b0154935061496b84612ec8565b92508964ffffffffff168364ffffffffff1611156149955761498e600182615ee0565b94506149ce565b8964ffffffffff168364ffffffffff1610156149bd576149b6816001615ecd565b95506149ce565b838497509750505050505050614a4f565b50614929565b8164ffffffffff168964ffffffffff161015614a25578961ffff166000036149fa578799505b8a61ffff6000198c018116908110614a1457614a14615bdd565b015483965096505050505050614a4f565b8760018b089950828b61ffff808d16908110614a4357614a43615bdd565b01549650965050505050505b94509492505050565b600080600080614a67886148c4565b67ffffffffffffffff1690506000614a7e896148da565b67ffffffffffffffff1690506000614a958a6148f0565b67ffffffffffffffff1690508664ffffffffff16600003614abc5791945092509050614bb2565b6000614ac78a6148c4565b67ffffffffffffffff1690506000614ade8b6148da565b67ffffffffffffffff1690506000614af58c6148f0565b67ffffffffffffffff1690508a64ffffffffff16600003614b205791975095509350614bb292505050565b6000614b3664ffffffffff808d16908e16615ecd565b9050808b64ffffffffff1685028d64ffffffffff1689020181614b5b57614b5b615c10565b049950808b64ffffffffff1684028d64ffffffffff1688020181614b8157614b81615c10565b049850808b64ffffffffff1683028d64ffffffffff1687020181614ba757614ba7615c10565b049750505050505050505b9450945094915050565b67016345785d8a0000816001600160801b031611156117c557604051638630bc6b60e01b815260040160405180910390fd5b6000806000614bfd8686615248565b91509150612c48868686858561519c565b600081600103614c215750607f19919050565b81600003614c425760405163125946cf60e11b815260040160405180910390fd5b60019190911c9060006f800000000000000000000000000000008310614c6a57506001614c85565b5060001982600160fe1b81614c8157614c81615c10565b0492505b6000614c94607f85901c61538d565b707f80000000000000000000000000000000607f82901b16935060ff16905083811c6f800000000000000000000000000000008114614d10576f400000000000000000000000000000005b6000811315614d0e57908002607f1c90600160801b8210614d06579384019360019190911c905b60011d614cdf565b505b50500260011b919050565b60008080614d338460581c67ffffffffffffffff1690565b9250614d498460181c67ffffffffffffffff1690565b91505062ffffff831672ffffffffffffffffffffffffffffffffffffff841180614d845750670de0b6b3a76400008367ffffffffffffffff16115b80614da05750670de0b6b3a76400008267ffffffffffffffff16115b1561137b57604051637752557f60e11b815260040160405180910390fd5b6000806001600160801b038516608086901c82614ddc838389615267565b905080600003614df6575060009350839250614a4f915050565b6000614e028a8961445e565b9050801580614e0f575086155b15614e2b57614e1d82615428565b899550955050505050614a4f565b614e36828883614bee565b95506000614e4587838a61549a565b905080831115614ee4576000614e5b8285615ee0565b9050600160801b8110614e9c57608081901c858111614e7a5780614e7c565b855b9050614e888187615ee0565b9550614e98608082901b83615ee0565b9150505b898110614ed0576000614eaf8b83615f60565b9050868111614ebe5780614ec0565b865b9050614ecc8188615ee0565b9650505b608085901b6001600160801b038716179a50505b7d09745258e83de0d0f4e400fce79aca44773dd596d31a6d336ca2a775b611614f16614f108d8d61243a565b8b61445e565b1115614f355760405163a2d7652760e01b815260040160405180910390fd5b50949997985050505050505050565b600081600003614f5657506000612c48565b6001600160801b038416608085901c600080614f8861075d614f788d8b61243a565b88614f83818c615ecd565b613445565b91509150836001600160801b0316826001600160801b03161115614feb576000614fce614fb58c8c613167565b614fbf8487615cc7565b6001600160801b0316906154b7565b9050614fe3816001600160801b031660801b90565b95505061502b565b826001600160801b0316816001600160801b0316111561502b57600061501e6150148c8c613167565b614fbf8588615cc7565b6001600160801b03169550505b505050509695505050505050565b8162ffffff168162ffffff161080156150555750608083901b15155b8061507a57508162ffffff168162ffffff1611801561507a57506001600160801b0383115b156109105760405163227bd87160e21b815262ffffff82166004820152602401611802565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b8117808214613553576000838152600287016020526040812082905582900361354757600883901c6000818152600180890160205260408220805460ff88169290921b821790559350839003613545578654600160ff83161b1787555060019350505050610735565b600080600052602060008351602085016000875af1905080801561516d573d80156151605760016000511483169250615167565b843b151592505b5061517d565b3d1561517d573d6000803e3d6000fd5b50806109105760405163197138bd60e11b815260040160405180910390fd5b6000816000036151bd578383816151b5576151b5615c10565b04905061277c565b8382106151dd576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000806000198385098385029250828110838203039150509250929050565b600083156152a257508083028184828161528357615283615c10565b04146152a2576040516363f1e01f60e01b815260040160405180910390fd5b82156115865760809290921b91820182811015611586576040516363f1e01f60e01b815260040160405180910390fd5b600061158683600184016154f8565b60008160801b80156152f557608091508092505b8260401b90508015615308579150604001815b8260201b9050801561531b579150602001815b8260101b9050801561532e579150601001815b8260081b90508015615341579150600801815b8260041b90508015615354579150600401815b8260021b90508015615367579150600201815b508160011b15615375576001015b60ff03919050565b6000611586836000198401615526565b60006001600160801b038211156153a65750608090811c905b67ffffffffffffffff8211156153be57604091821c91015b63ffffffff8211156153d257602091821c91015b61ffff8211156153e457601091821c91015b60ff8211156153f557600891821c91015b600f82111561540657600491821c91015b600382111561541757600291821c91015b60018211156133bf57600101919050565b60008160000361543a57506000919050565b60006154458361538d565b600181811c607f1681811b9086901c01811c80860401811c80860401811c80860401811c80860401811c80860401811c80860401901c9384900493925060ff1690508282106154945782611586565b50919050565b60006154a7848484614bee565b9050818061311e5761311e615c10565b6000816154c381614bbc565b6ec097ce7bc90715b34b9f1000000000806001600160801b03868116908616908102670de0b6b3a764000091909101026131c3565b60ff81169190911c906000821561551c5781615513846152e1565b0160ff16611586565b5060001992915050565b60ff8181031691821b91600090831561554c57806155438561538d565b60ff16036122b3565b506000199392505050565b6001600160a01b03811681146117c557600080fd5b6000806040838503121561557f57600080fd5b823561558a81615557565b946020939093013593505050565b60005b838110156155b357818101518382015260200161559b565b50506000910152565b60208152600082518060208401526155db816040850160208701615598565b601f01601f19169190910160400192915050565b803562ffffff811681146133bf57600080fd5b60006020828403121561561457600080fd5b611586826155ef565b60008083601f84011261562f57600080fd5b50813567ffffffffffffffff81111561564757600080fd5b6020830191508360208260051b8501011115612eb557600080fd5b6000806000806000806080878903121561567b57600080fd5b863561568681615557565b9550602087013561569681615557565b9450604087013567ffffffffffffffff808211156156b357600080fd5b6156bf8a838b0161561d565b909650945060608901359150808211156156d857600080fd5b506156e589828a0161561d565b979a9699509497509295939492505050565b60008083601f84011261570957600080fd5b50813567ffffffffffffffff81111561572157600080fd5b602083019150836020828501011115612eb557600080fd5b60008060006040848603121561574e57600080fd5b83359250602084013567ffffffffffffffff81111561576c57600080fd5b615778868287016156f7565b9497909650939450505050565b6000806000806060858703121561579b57600080fd5b84356157a681615557565b9350602085013567ffffffffffffffff8111156157c257600080fd5b6157ce8782880161561d565b90945092505060408501356157e281615557565b939692955090935050565b600081518084526020808501945080840160005b8381101561581d57815187529582019590820190600101615801565b509495945050505050565b83815282602082015260606040820152600061277c60608301846157ed565b803561ffff811681146133bf57600080fd5b600080600080600080600080610100898b03121561587657600080fd5b61587f89615847565b975061588d60208a01615847565b965061589b60408a01615847565b95506158a960608a01615847565b94506158b760808a016155ef565b93506158c560a08a01615847565b92506158d360c08a016155ef565b91506158e160e08a016155ef565b90509295985092959890939650565b6000806000806040858703121561590657600080fd5b843567ffffffffffffffff8082111561591e57600080fd5b61592a8883890161561d565b9096509450602087013591508082111561594357600080fd5b506159508782880161561d565b95989497509550505050565b60208152600061158660208301846157ed565b803580151581146133bf57600080fd5b6000806040838503121561599257600080fd5b61599b8361596f565b915060208301356159ab81615557565b809150509250929050565b600080600080600080600060e0888a0312156159d157600080fd5b6159da88615847565b96506159e860208901615847565b95506159f660408901615847565b9450615a0460608901615847565b9350615a12608089016155ef565b9250615a2060a08901615847565b9150615a2e60c089016155ef565b905092959891949750929550565b600060208284031215615a4e57600080fd5b813564ffffffffff8116811461158657600080fd5b60008060408385031215615a7657600080fd5b615a7f8361596f565b9150612eb2602084016155ef565b60008060408385031215615aa057600080fd5b82356001600160801b0381168114615ab757600080fd5b9150612eb26020840161596f565b600060208284031215615ad757600080fd5b5035919050565b600060208284031215615af057600080fd5b61158682615847565b60008060408385031215615b0c57600080fd5b8235615ab781615557565b60008060408385031215615b2a57600080fd5b823561599b81615557565b60008060008060608587031215615b4b57600080fd5b8435615b5681615557565b935060208501359250604085013567ffffffffffffffff811115615b7957600080fd5b615950878288016156f7565b600060208284031215615b9757600080fd5b815161158681615557565b634e487b7160e01b600052604160045260246000fd5b604081526000615bcb60408301856157ed565b828103602084015261277c81856157ed565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615c0557600080fd5b813561158681615557565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff8281168282160390808211156131c8576131c8615c26565b67ffffffffffffffff818116838216028082169190828114615c7e57615c7e615c26565b505092915050565b67ffffffffffffffff8181168382160190808211156131c8576131c8615c26565b6001600160801b038181168382160190808211156131c8576131c8615c26565b6001600160801b038281168282160390808211156131c8576131c8615c26565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115615d1957600080fd5b8260051b80836020870137939093016020019392505050565b604081526000615d46604083018688615ce7565b828103602084015261299b818587615ce7565b604081526000615d6d604083018587615ce7565b8281036020840152612c4881856157ed565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006001600160a01b03808a168352808916602084015280881660408401525085606083015284608083015260c060a0830152613f6f60c083018486615d7f565b60008251615dfb818460208701615598565b9190910192915050565b600060208284031215615e1757600080fd5b5051919050565b60006001600160a01b03808a168352808916602084015280881660408401525060a06060830152615e5360a083018688615ce7565b8281036080840152615e66818587615ce7565b9a9950505050505050505050565b83815260406020820152600061277c604083018486615d7f565b60006001600160a01b03808816835280871660208401525060806040830152615ebb608083018587615ce7565b90508260608301529695505050505050565b8082018082111561073557610735615c26565b8181038181111561073557610735615c26565b64ffffffffff8181168382160190808211156131c8576131c8615c26565b600061ffff80841680615f2657615f26615c10565b92169190910692915050565b600082615f4157615f41615c10565b600160ff1b821460001984141615615f5b57615f5b615c26565b500590565b600082615f6f57615f6f615c10565b500490565b61ffff8281168282160390808211156131c8576131c8615c2656fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122021d0d6a98b619f239edd28c667566f18472738c97ca0f112816c25af22bc051764736f6c63430008140033000000000000000000000000b43120c4745967fa9b93e79c149e66b0f2d6fe0c
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102765760003560e01c806388cc58e411610160578063c9939f5e116100d8578063e584b6541161008c578063e985e9c511610071578063e985e9c5146106c9578063ea3446bf146106ec578063f5e29329146106ff57600080fd5b8063e584b654146106a3578063e77366f8146106b657600080fd5b8063d8dfcea0116100bd578063d8dfcea01461067b578063da10610c14610683578063dbe65edc1461069b57600080fd5b8063c9939f5e14610660578063d3b9fbe41461067357600080fd5b8063a1af5b9a1161012f578063abcd783011610114578063abcd7830146105f0578063bd85b0391461062d578063c7bd65861461064d57600080fd5b8063a1af5b9a146105c1578063a41a01fb146105c957600080fd5b806388cc58e4146105035780638940a16a146105295780638d7024e51461056757806395d89b41146105a257600080fd5b806347973bff116101f357806355182894116101c25780636653851a116101a75780636653851a14610494578063781a8915146104a75780637ca0de30146104b157600080fd5b806355182894146104285780635c60da1b1461046d57600080fd5b806347973bff146103cf5780634c7cffbd146103e25780634e1273f4146103f557806353c059a01461041557600080fd5b80630abe96881161024a57806317fad7fc1161022f57806317fad7fc146103855780631b057f6d1461039a578063383d15c5146103ad57600080fd5b80630abe96881461033457806317f11ecc1461036a57600080fd5b8062fdd58e1461027b57806305e8746d146102a157806306fdde03146102ca5780630902f1ac1461030c575b600080fd5b61028e61028936600461556c565b610712565b6040519081526020015b60405180910390f35b3660011981013560f01c90033560601c5b6040516001600160a01b039091168152602001610298565b60408051808201909152601481527f4c697175696469747920426f6f6b20546f6b656e00000000000000000000000060208201525b60405161029891906155bc565b610314610740565b604080516001600160801b03938416815292909116602083015201610298565b610314610342366004615602565b62ffffff166000908152600660205260409020546001600160801b0381169160809190911c90565b61037261077a565b60405161ffff9091168152602001610298565b610398610393366004615662565b61079b565b005b6103986103a8366004615739565b6107f6565b6103c06103bb366004615785565b610915565b60405161029893929190615828565b6103986103dd366004615859565b610b9b565b61028e6103f0366004615602565b610cf9565b6104086104033660046158f0565b610d29565b604051610298919061595c565b61028e61042336600461597f565b610dff565b610430611138565b6040805160ff909616865261ffff9485166020870152939092169284019290925264ffffffffff918216606084015216608082015260a001610298565b6102b27f0000000000000000000000007a5b4e301fc2b148cefe57257a236eb84508279781565b6103986104a23660046159b6565b6111e2565b620100095461028e565b6104b961121b565b6040805161ffff9889168152968816602088015294871694860194909452918516606085015262ffffff9081166080850152931660a08301529190911660c082015260e001610298565b7f000000000000000000000000b43120c4745967fa9b93e79c149e66b0f2d6fe0c6102b2565b61053c610537366004615a3c565b611283565b6040805167ffffffffffffffff94851681529284166020840152921691810191909152606001610298565b61056f611382565b6040805162ffffff95861681529385166020850152919093169082015264ffffffffff9091166060820152608001610298565b60408051808201909152600381526213109560ea1b60208201526102ff565b61028e6113c2565b6105dc6105d7366004615a63565b61157a565b60405162ffffff9091168152602001610298565b6106036105fe366004615a8d565b61158d565b604080516001600160801b0394851681529284166020840152921691810191909152606001610298565b61028e61063b366004615ac5565b60009081526001602052604090205490565b61039861065b366004615ade565b611732565b61040861066e366004615662565b6117c8565b610398611af2565b610314611b86565b3660011981013560f01c90036014013560601c6102b2565b6105dc611ba1565b6103986106b1366004615af9565b611bae565b6106036106c4366004615a8d565b611bbd565b6106dc6106d7366004615b17565b611d03565b6040519015158152602001610298565b6103986106fa366004615b35565b611d0f565b6105dc61070d366004615ac5565b611fed565b6001600160a01b0382166000908152602081815260408083208484529091529020545b92915050565b905090565b60008061077161075d60055460045461201890919063ffffffff16565b6001600160801b0381169160809190911c90565b90939092509050565b600061073b60003660011981013560f090811c90910360280135901c905090565b6107a3612052565b62010009546107b8813389898989898961208a565b6107c687878787878761212a565b6107dd6001600080516020615f9083398151915255565b6107ed8133898989898989612188565b50505050505050565b6107fe612052565b6108066121cf565b62010009839055604080518481529051849133917f4d24d81a1a6e09c37497747b4c9d67d858ce80438b54df0fa361a24dc742d9349181900360200190a26001600160a01b038116158015906108cf5750306001600160a01b0316816001600160a01b0316635f9c01b16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561089f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c39190615b85565b6001600160a01b031614155b156108ed57604051631b5de6eb60e01b815260040160405180910390fd5b6108f8848484612218565b506109106001600080516020615f9083398151915255565b505050565b6000806060866109248161223f565b61092c612052565b600086900361094e5760405163296db0d960e01b815260040160405180910390fd5b62010009546040805160608101909152600090808967ffffffffffffffff81111561097b5761097b615ba2565b6040519080825280602002602001820160405280156109a4578160200160208202803683370190505b5081526020018967ffffffffffffffff8111156109c3576109c3615ba2565b6040519080825280602002602001820160405280156109ec578160200160208202803683370190505b5081526020018967ffffffffffffffff811115610a0b57610a0b615ba2565b604051908082528060200260200182016040528015610a34578160200160208202803683370190505b509052600454909150610a60813660011981013560f01c90038035606090811c9160140135901c61227b565b9650610a7083338d8d8d8c6122bb565b610a7d8a8a898e86612305565b9550610a93610a8c8888612018565b829061243a565b600455604080830151835191519096506001600160a01b038d169160009133917f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb91610ae191908b90615bb8565b60405180910390a4815160208301516040516001600160a01b038e169233927f87f1f9dcf5e8089a3e00811b6a008d8f30293a3da878cb1fe8c90ca376402f8a92610b2d929190615bb8565b60405180910390a38515610b5f57610b5f863660011981013560f01c90038035606090811c9160140135901c8b612474565b610b766001600080516020615f9083398151915255565b610b8d83338d8d8d610b888d8d612018565b6124d2565b505050509450945094915050565b610ba36121cf565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610bee5750825b905060008267ffffffffffffffff166001148015610c0b5750303b155b905081158015610c19575080155b15610c375760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610c6b57845468ff00000000000000001916680100000000000000001785555b610c73612514565b610c9f610c93610c8e8860035461252490919063ffffffff16565b612573565b8e8e8e8e8e8e8e6125c7565b8315610cea57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b6000610735610d1d60003660011981013560f090811c90910360280135901c905090565b62ffffff84169061274e565b60608382610d378282612785565b8567ffffffffffffffff811115610d5057610d50615ba2565b604051908082528060200260200182016040528015610d79578160200160208202803683370190505b50925060005b86811015610df457610dcf888883818110610d9c57610d9c615bdd565b9050602002016020810190610db19190615bf3565b878784818110610dc357610dc3615bdd565b90506020020135610712565b848281518110610de157610de1615bdd565b6020908102919091010152600101610d7f565b505050949350505050565b6000610e09612052565b6201000954600454600554600086610e3d57610e383660011981013560f01c90036014013560601c84906127a5565b610e57565b610e573660011981013560f01c90033560601c84906127da565b90506000819003610e7b57604051637df801c760e01b815260040160405180910390fd5b86610e898533898486612809565b610e93848361243a565b6003549094506000610eba60003660011981013560f090811c90910360280135901c905090565b90506000610ec78361286d565b9050610ed38342612879565b92505b62ffffff8116600090815260066020526040902054610ef68186156128ef565b61101a57610f04848361291e565b935060008080610f188488888b898e6129a6565b91945092509050821561101657610f2f8984612018565b9850610f3b8d8361243a565b9c506000610f56610f4b89612bcc565b839061ffff16612bdc565b90508015610f7757610f688b8261243a565b9a50610f748482612018565b93505b610f8b83610f85878761243a565b90612018565b62ffffff87166000908152600660205260409020556001600160a01b038f16337fad7d6f97abf51ce18e17a38f4d70e975be9c0708474987bb3e26ad21bd93ca70888787610fd88e612c52565b6040805162ffffff9586168152602081019490945283019190915290911660608201526080810186905260a0810185905260c00160405180910390a3505b5050505b60008690036110295750611073565b60006110358684612c63565b905062ffffff8116158061104d575062ffffff818116145b1561106b57604051631a6d7fb160e31b815260040160405180910390fd5b915050610ed6565b60008990036110955760405163439df85d60e11b815260040160405180910390fd5b61109f878a612018565b60045560058690556110b3600a8483612c85565b92506110bf8382612524565b60035583156110eb576110e63660011981013560f01c90036014013560601c8a908c612da0565b611106565b6111063660011981013560f01c90033560601c8a908c612de0565b61111d6001600080516020615f9083398151915255565b61112a88338c878d612df1565b505050505050505092915050565b6003546078906000908190819081908161115182612e4e565b905061ffff8116156111d957600061116a600a83612e5e565b9650905061117781612ebc565b965061118281612ec8565b94508464ffffffffff1660000361119857600095505b61ffff8616156111d7576111c98661ffff168361ffff16816111bc576111bc615c10565b600a916001910601612ee9565b90506111d481612ec8565b93505b505b50509091929394565b6111ea612052565b6111f26121cf565b611204600354888888888888886125c7565b6107ed6001600080516020615f9083398151915255565b600080600080600080600080600354905061123581612ebc565b975061124081612f19565b965061124b81612f29565b955061125681612f39565b945061126181612f49565b935061126c81612bcc565b925061127781612f5a565b91505090919293949596565b6000806000806003549050600061129982612e4e565b905061ffff811615806112b25750428664ffffffffff16115b156112c9576000806000945094509450505061137b565b60006112d7600a8389612f6b565b91985096509450905064ffffffffff80881690821610156113775761130d6112fe8461286d565b849064ffffffffff8a1661308e565b9250600061131b8289615c3c565b90508064ffffffffff1661132e8561286d565b62ffffff1661133d9190615c5a565b6113479088615c86565b96508064ffffffffff1661135a85612c52565b62ffffff166113699190615c5a565b6113739087615c86565b9550505b5050505b9193909250565b6000806000806000600354905061139881612c52565b94506113a3816130a6565b93506113ae816130b7565b92506113b9816130c8565b91505090919293565b60006113cc612052565b7f000000000000000000000000b43120c4745967fa9b93e79c149e66b0f2d6fe0c6001600160a01b0316634ccb20c06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561142a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144e9190615b85565b6001600160a01b0316336001600160a01b03161461147f57604051630f8937c560e21b815260040160405180910390fd5b6005546001600160801b038116608082901c60006114da826114a25760006114a5565b60015b60ff166000856001600160801b0316116114c05760006114c3565b60015b60ff169060801b6001600160801b03919091161790565b90506114e68482612018565b9450841561155c5760058190556004546115009086612018565b60045560405185815233907f3f41a5ddc53701cc7db577ade4f1fca9838a8ec0b5ea50b9f0f5d17bc4554e329060200160405180910390a261155c853660011981013560f01c90038035606090811c9160140135901c33612474565b505050506115776001600080516020615f9083398151915255565b90565b60006115868383612c63565b9392505050565b60035460009083908290816115b760003660011981013560f090811c90910360280135901c905090565b905060006115c48361286d565b90506115d08342612879565b92505b62ffffff81166000908152600660205260408120546115f39089156130db565b90506001600160801b038116156116d957600061161562ffffff84168561274e565b90506000876001600160801b0316836001600160801b031611611638578261163a565b875b9050611646868561291e565b955060008a611669576116646001600160801b0383168460806130fb565b61167e565b61167e6001600160801b038316608085613131565b9050600061168c8888613167565b905060006116a36001600160801b03841683613186565b90506116af8184615ca7565b6116b9908d615ca7565b9b506116c5848c615cc7565b9a506116d1818b615ca7565b995050505050505b856001600160801b03166000036116f05750611728565b60006116fc8984612c63565b905062ffffff81161580611714575062ffffff818116145b15611720575050611728565b9150506115d3565b5050509250925092565b61173a612052565b600354600061174882612e4e565b90508061ffff166000036117675750600161176382826131cf565b6003555b611773600a828561321e565b60405161ffff8416815233907fc975541e72d695746a43ba65745d79963a23082637c8f4609354d9bcf70194d69060200160405180910390a250506117c56001600080516020615f9083398151915255565b50565b606086336117d68282613307565b61180b5760405163548f773d60e01b81526001600160a01b038084166004830152821660248201526044015b60405180910390fd5b611813612052565b8515806118205750858414155b1561183e57604051633ab7870760e01b815260040160405180910390fd5b620100095461185381338c8c8c8c8c8c613353565b898767ffffffffffffffff81111561186d5761186d615ba2565b604051908082528060200260200182016040528015611896578160200160208202803683370190505b5094506000805b898110156119ea5760006118c88c8c848181106118bc576118bc615bdd565b90506020020135613399565b905060008a8a848181106118de576118de615bdd565b9050602002013590508060000361190f57604051636996a92560e01b815262ffffff83166004820152602401611802565b62ffffff8216600081815260066020908152604080832054600190925290912054909161193e908890856133c4565b600061194b838584613445565b905060008190036119765760405163fd44792960e01b815262ffffff86166004820152602401611802565b6119808382612018565b9250838203611996576119946007866134bd565b505b62ffffff851660009081526006602052604090208390558b5181908d90889081106119c3576119c3615bdd565b60209081029190910101526119d8878261243a565b9650856001019550505050505061189d565b506004546119f89082612018565b6004556040516000906001600160a01b0384169033907f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb90611a41908f908f908f908f90615d32565b60405180910390a48a6001600160a01b0316336001600160a01b03167fa32e146844d6144a22e94c586715a1317d58a8aa3581ec33d040113ddcb243508c8c8a604051611a9093929190615d59565b60405180910390a3611abc813660011981013560f01c90038035606090811c9160140135901c8e612474565b611ad36001600080516020615f9083398151915255565b611ae38333848e8e8e8e8e61355f565b50505050509695505050505050565b611afa612052565b611b026121cf565b600354611b16611b1182612573565b6135a5565b600355337f282afaeeae84c1d85ad1424a3aa2ddbedaeefca3b1e53d889d15265fe44db7fc611b44836130b7565b611b4d846130a6565b6040805162ffffff93841681529290911660208301520160405180910390a250611b846001600080516020615f9083398151915255565b565b60055460009081906001600160801b0381169060801c610771565b600061073b60035461286d565b611bb93383836135da565b5050565b6000808080611bd56001600160801b0387168661368f565b6003549091506000611bfc60003660011981013560f090811c90910360280135901c905090565b90506000611c098361286d565b9050611c158342612879565b92505b62ffffff8116600090815260066020526040902054611c38818a156128ef565b611ca557611c46848361291e565b935060008080611c5a8488888f898d6129a6565b919450925090508215611ca157611c718884612018565b9750611c7e828d156130db565b611c88908b615ca7565b9950611c94818d6130db565b611c9e908a615ca7565b98505b5050505b6000859003611cb45750611cec565b6000611cc08a84612c63565b905062ffffff81161580611cd8575062ffffff818116145b15611ce4575050611cec565b915050611c18565b611cf684896130db565b9650505050509250925092565b60006115868383613307565b611d17612052565b6000839003611d395760405163727ab02160e11b815260040160405180910390fd5b62010009546004546000611d4c866136a0565b9050611d5a833389896137a8565b611d7e863660011981013560f01c90038035606090811c9160140135901c8a612474565b6000806001600160a01b038916631faa6b8760e01b333660011981013560f01c90033560601c3660011981013560f01c90036014013560601c8c888d8d604051602401611dd19796959493929190615da8565b60408051601f198184030181529181526020820180516001600160e01b03167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611e279190615de9565b6000604051808303816000865af19150503d8060008114611e64576040519150601f19603f3d011682016040523d82523d6000602084013e611e69565b606091505b5091509150811580611e7d57508051602014155b80611ebb57507fab5c473bce5960a8292e9c8db82f9272504caab4e9ef09553fc112f82b62a3c281806020019051810190611eb89190615e05565b14155b15611ed957604051634a57011360e01b815260040160405180910390fd5b6000611efe813660011981013560f01c90038035606090811c9160140135901c61227b565b9050611f14611f0d868661243a565b82906137fd565b15611f3257604051630e3c717160e11b815260040160405180910390fd5b6000611f3e8287612018565b6004839055600554909150611f53908261243a565b6005556003546001600160a01b038c169033907fd126bd9d94daca8e55ffd8283fac05394aec8326c6b1639e1e8a445fbe8bbc7d90611f919061286d565b6040805162ffffff9092168252602082018f905281018590526060810185905260800160405180910390a3611fd36001600080516020615f9083398151915255565b611fe087338d8885613841565b5050505050505050505050565b600061073561201160003660011981013560f090811c90910360280135901c905090565b839061389d565b8082038281118061203457506001600160801b03808416908216115b156107355760405163e599af5560e01b815260040160405180910390fd5b600080516020615f9083398151915280546001190161208457604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b750100000000000000000000000000000000000000000088161561212057612120886379c8ccf760e01b898989898989896040516024016120d19796959493929190615e1e565b60408051601f198184030181529190526020810180516001600160e01b03167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526138e2565b5050505050505050565b85336121368282613307565b6121665760405163548f773d60e01b81526001600160a01b03808416600483015282166024820152604401611802565b612120888888888888613946565b6001600080516020615f9083398151915255565b750200000000000000000000000000000000000000000088161561212057612120886367ddb27860e01b898989898989896040516024016120d19796959493929190615e1e565b336001600160a01b037f000000000000000000000000b43120c4745967fa9b93e79c149e66b0f2d6fe0c1614611b8457604051632486085360e01b815260040160405180910390fd5b82156109105761091083634e430b5a60e01b8585856040516024016120d193929190615e74565b6001600160a01b038116158061225d57506001600160a01b03811630145b156117c5576040516345c210e760e11b815260040160405180910390fd5b60006122b384610f8561228d85613aa9565b61229687613aa9565b6001600160801b03169060801b6001600160801b03919091161790565b949350505050565b741000000000000000000000000000000000000000008616156122fd576122fd86630d4abdb360e01b87878787876040516024016120d1959493929190615e8e565b505050505050565b60008061232760003660011981013560f090811c90910360280135901c905090565b60035490915060006123388261286d565b905086935060005b8881101561242d576000806123768a8d8d8681811061236157612361615bdd565b90506020020135613b1890919063ffffffff16565b91509150600080600061238c898886888c613b61565b9194509250905061239d8a83612018565b99508362ffffff168b6000015187815181106123bb576123bb615bdd565b602002602001018181525050808b6020015187815181106123de576123de615bdd565b602002602001018181525050828b60400151878151811061240157612401615bdd565b60200260200101818152505061241d8c8562ffffff1685613d6f565b8560010195505050505050612340565b5050505095945050505050565b8181018281108061245657506001600160801b03808416908216105b15610735576040516316692de960e01b815260040160405180910390fd5b6001600160801b038416608085901c81156124a6576124a66001600160a01b038616846001600160801b038516613dbc565b6001600160801b038116156122fd576122fd6001600160a01b038516846001600160801b038416613dbc565b742000000000000000000000000000000000000000008616156122fd576122fd86637a51a4c660e01b87878787876040516024016120d1959493929190615e8e565b61251c613e0d565b611b84613e5b565b60007cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83167fffffff000000000000000000000000000000000000000000000000000000000060e884901b1617611586565b60008061257f8361286d565b90507fffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffff831675ffffff00000000000000000000000000000000000000609883901b1617611586565b61ffff87161580156125db575061ffff8616155b80156125e9575061ffff8516155b80156125f7575061ffff8416155b8015612606575062ffffff8316155b8015612614575061ffff8216155b8015612623575062ffffff8116155b15612641576040516331375e0360e11b815260040160405180910390fd5b6126518888888888888888613e63565b9750600061267460003660011981013560f090811c90910360280135901c905090565b905060006126828a84613f7c565b905060006126908284613fda565b61269a838561401a565b6126a49190615ecd565b905067016345785d8a00008111156126cf576040516372db9c0b60e11b815260040160405180910390fd5b50505060038890556040805161ffff8981168252888116602083015287811682840152868116606083015262ffffff868116608084015290851660a0830152831660c0820152905133917fd09e5ddc721ff14c5c1e66a305cbba1fd70b82c5232bc391aad6f55e62e4b046919081900360e00190a25050505050505050565b600061271061ffff60801b608084901b1604600160801b0162ffffff8416627fffff190161277c828261403e565b95945050505050565b808214611bb9576040516340311ffd60e11b815260040160405180910390fd5b6000806127b28460801c90565b90506122b3816127c185613aa9565b6127cb9190615cc7565b6001600160801b031660801b90565b60006001600160801b0383166122b3816127f385613aa9565b6127fd9190615cc7565b6001600160801b031690565b74010000000000000000000000000000000000000000851615612866576040516001600160a01b0380861660248301528416604482015282151560648201526084810182905261286690869063feee373560e01b9060a4016120d1565b5050505050565b600060e882901c610735565b600080612885846130c8565b6128969064ffffffffff1684615ee0565b90506128a184612f19565b61ffff1681106128e5576128b484612573565b93506128bf84612f29565b61ffff1681106128d9576128d484600061429e565b6128e2565b6128e2846135a5565b93505b6122b3848461430c565b60008161290f576129008360801c90565b6001600160801b031615611586565b50506001600160801b03161590565b60008061292a846130b7565b62ffffff169050600080828562ffffff161161294d578462ffffff168303612956565b828562ffffff16035b91506127108202612966876130a6565b62ffffff16019050600061297987612f5a565b62ffffff16905080821161298d578161298f565b805b915061299b8783613f7c565b979650505050505050565b60008080806129ba62ffffff87168961274e565b905060006129c98b89156130db565b90506000886129f4576129ef6129ea6001600160801b0384168560806130fb565b614365565b612a0c565b612a0c6129ea6001600160801b038416608086613131565b90506000612a1a8c8c613167565b90506000612a316001600160801b03841683613186565b9050612a3d8184615ca7565b92506000612a4b8a8d6130db565b9050600080856001600160801b0316836001600160801b031610612a76575084915082905085612aec565b612a896001600160801b0384168661438f565b91506000612a978385615cc7565b6001600160801b031690508e612abb57612ab66129ea8260808c6143c6565b612aca565b612aca6129ea828b60806143f0565b9150876001600160801b0316826001600160801b03161115612aea578791505b505b8d612b2657612b04836001600160801b031660801b90565b6001600160801b038216612b21846001600160801b031660801b90565b612b4e565b6001600160801b038316612b43826001600160801b031660801b90565b6001600160801b0384165b809b50819c50829d50505050505050505050507d09745258e83de0d0f4e400fce79aca44773dd596d31a6d336ca2a775b611612ba0612b9a85610f85888f61243a90919063ffffffff16565b8361445e565b1115612bbf5760405163a2d7652760e01b815260040160405180910390fd5b5096509650969350505050565b600061073582604e1c613fff1690565b6000816001600160801b0316600003612bf757506000610735565b6127106001600160801b038316811015612c2457604051631514a94960e21b815260040160405180910390fd5b6001600160801b03848116840282900490608086811c86028490049183169082901b175b9695505050505050565b60006107358260701c620fffff1690565b600082612c7a57612c7560078361447a565b611586565b6115866007836145c6565b600080612c9184612e4e565b90508061ffff16600003612ca85783915050611586565b6000612cb48683612ee9565b90506000612cc1826146f6565b90506000612cce83614702565b612cdb9060ff1683615ef3565b90508064ffffffffff16612cee42614711565b64ffffffffff161115612d94576000806000612d338464ffffffffff164203612d168c61286d565b612d1f8d612c52565b612d298e8e614739565b8a93929190614769565b9250925092506000612d4487612ebc565b905064ffffffffff861642036078811115612d74575096879006600101964295506000612d718c8a6131cf565b9b505b612d8282868686858c6147ce565b97505050505050612d94888585614872565b50949695505050505050565b6000612dac8460801c90565b90506001600160801b03811615612dda57612dda6001600160a01b038416836001600160801b038416613dbc565b50505050565b60006001600160801b038416612dac565b74020000000000000000000000000000000000000000851615612866576040516001600160a01b038086166024830152841660448201528215156064820152608481018290526128669086906353e8079b60e01b9060a4016120d1565b60006107358260d81c61ffff1690565b600080612e6b8484612ee9565b9150612e7682612ebc565b90508061ffff168361ffff1614612eb557612e99612e948583612ee9565b612ebc565b90508061ffff168361ffff1611612eb05780612eb2565b825b90505b9250929050565b600061ffff8216610735565b6000612ed382614702565b60ff16612edf836146f6565b6107359190615ef3565b600081612ef58161489f565b8361ffff60001985018116908110612f0f57612f0f615bdd565b0154949350505050565b60006107358260101c610fff1690565b600061073582601c1c610fff1690565b60006107358260281c613fff1690565b60006107358260361c62ffffff1690565b600061073582605c1c620fffff1690565b600080600080600080612f7e8989612e5e565b909250905064ffffffffff8716612fb58a612f99848c615f11565b61ffff1661ffff8110612fae57612fae615bdd565b0154612ec8565b64ffffffffff161115612fdb57604051631c4e7f9560e21b815260040160405180910390fd5b612fe482612ec8565b95508664ffffffffff168664ffffffffff16116130265785613005836148c4565b61300e846148da565b613017856148f0565b95509550955095505050613085565b8695506000806130388b8b8b86614906565b9150915060008961304883612ec8565b6130529190615c3c565b9050600061305f84612ec8565b613069908c615c3c565b905061307784848484614a58565b919a50985096505050505050505b93509350935093565b600061309a8483612879565b93506122b3848461291e565b60006107358260841c620fffff1690565b60006107358260981c62ffffff1690565b60006107358260b01c64ffffffffff1690565b6000816130ec57612c758360801c90565b6001600160801b038316611586565b60006131088484846143f0565b9050600160ff83161b8061311e5761311e615c10565b83850915611586576122b3600182615ecd565b600061313e8484846143c6565b9050818061314e5761314e615c10565b600160ff85161b850915611586576122b3600182615ecd565b60006115866131768484613fda565b613180858561401a565b01614365565b60008161319281614bbc565b6001600160801b03808416670de0b6b3a764000081900391829160001991881602820101816131c3576131c3615c10565b049250505b5092915050565b60007fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff83167cffff00000000000000000000000000000000000000000000000000000060d884901b1617611586565b600061322a8484612ee9565b9050600061323782612ebc565b90508261ffff168161ffff161061326157604051632c57574160e21b815260040160405180910390fd5b60008461ffff168261ffff16146132925761ffff82161561328b576132868683612ee9565b613294565b6000613294565b825b905060006132a182612ebc565b61ffff169050808661ffff16116132b857806132be565b8561ffff165b905061ffff83165b8561ffff168110156132f05781888261ffff81106132e6576132e6615bdd565b01556001016132c6565b506107ed878761ffff868116881890891617614872565b6000816001600160a01b0316836001600160a01b031614806115865750506001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b74400000000000000000000000000000000000000000881615612120576121208863ea9db9d360e01b898989898989896040516024016120d19796959493929190615e1e565b8062ffffff811681146133bf57604051639b63641560e01b815260040160405180910390fd5b919050565b6001600160a01b03831660009081526020818152604080832085845291829052909120548281101561342157604051625b387760e31b81526001600160a01b03861660048201526024810185905260448101849052606401611802565b60009384526001602090815260408086208054869003905592905292209103905550565b60006001600160801b038416608085901c82808315613478576134756129ea886001600160801b03871689614bee565b91505b6001600160801b038316156134a15761349e6129ea886001600160801b03861689614bee565b90505b608081901b6001600160801b0383161798975050505050505050565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b19811680821461355357600083815260028701602052604081208290558190036135475750600882901c6000818152600187810160205260408220805460ff87169290921b19909116908190559190829003613545578654600160ff83161b191687555b505b60019350505050610735565b50600095945050505050565b74800000000000000000000000000000000000000000881615612120576121208863fdc2725760e01b898989898989896040516024016120d19796959493929190615e1e565b6000806135b183612c52565b62ffffff16905060006135c384612f39565b61ffff1690506127108282020461277c858261429e565b826135e48161223f565b826001600160a01b0316846001600160a01b0316036136215760405163782ee70760e01b81526001600160a01b0385166004820152602401611802565b6001600160a01b03848116600081815260026020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b6000816130ec57612c758360801b90565b6000807f000000000000000000000000b43120c4745967fa9b93e79c149e66b0f2d6fe0c6001600160a01b031663fd90c2be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613701573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137259190615e05565b90506001600160801b0383811690608085901c90670de0b6b3a763ffff9061375e90670de0b6b3a7640000908616850283015b04614365565b9250613790670de0b6b3a764000082866001600160801b0316856001600160801b031602018161375857613758615c10565b915050608081901b6001600160801b0383161761277c565b74040000000000000000000000000000000000000000841615612dda576040516001600160a01b0380851660248301528316604482015260648101829052612dda90859063505d903360e01b906084016120d1565b60006001600160801b0383811690608085811c9185169085901c8184108061299b5750806001600160801b0316836001600160801b03161094505050505092915050565b74080000000000000000000000000000000000000000851615612866576040516001600160a01b0380861660248301528416604482015260648101839052608481018290526128669086906360bed5f360e01b9060a4016120d1565b600061271061ffff60801b608084901b1604600160801b01816138bf82614c0e565b6138c886614c0e565b6138d29190615f32565b905061277c816280000001613399565b600080839050602083015160e01c602060008551602087016000865af192503d151583151615613916573d6000803e3d6000fd5b8060005160e01c14601f3d1116831692505081612dda57604051636c93cb9b60e01b815260040160405180910390fd5b82816139528282612785565b8661395c8161223f565b6001600160a01b03808a16600090815260208190526040808220928b1682528120905b88811015613a405760008a8a8381811061399b5761399b615bdd565b90506020020135905060008989848181106139b8576139b8615bdd565b600085815260208981526040909120549102929092013592505081811015613a15578e83836040516358b4690f60e01b8152600401611802939291906001600160a01b039390931683526020830191909152604082015260600190565b600092835260208681526040808520928490039092558590529091208054909101905560010161397f565b50896001600160a01b03168b6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8c8c8c8c604051613a949493929190615d32565b60405180910390a45050505050505050505050565b6040516370a0823160e01b8152306004820152600090610735906001600160a01b038416906370a0823190602401602060405180830381865afa158015613af4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ea9190615e05565b6000806000806000613b2987614d1b565b670de0b6b3a764000060808a811c90930281900490921b6001600160801b03998a169093029190910490971617979596505050505050565b62ffffff83166000818152600660205260408120549091829182918290613b88908b61274e565b90506000613ba78962ffffff1660009081526001602052604090205490565b9050613bb583898484614dbe565b909650945084935062ffffff808b16908a1603613ced57613bd7878a4261308e565b96506000613be984898e89868c614f44565b90508015613ce7576000613c0784613c018985612018565b9061445e565b90506000613c22613c178b612bcc565b849061ffff16612bdc565b90508015613c4857613c348782612018565b600554909750613c44908261243a565b6005555b6000613c6286613c01613c5b8786612018565b8a9061243a565b9050613c6f838683614bee565b9950613c7d600a8c8f612c85565b9a508a600381905550336001600160a01b03167f3f0b46725027bb418b2005f4683538eccdbcdf1de2b8649a29dbd9c507d16ff48e8685604051613cdb9392919062ffffff9390931683526020830191909152604082015260600190565b60405180910390a25050505b50613cf8565b613cf8858b8b615039565b851580613d03575083155b15613d2857604051634c98d35760e11b815262ffffff8a166004820152602401611802565b80600003613d3d57613d3b60078a61509f565b505b613d47838561243a565b62ffffff90991660009081526006602052604090209890985550929891975095509350505050565b60008281526001602052604081208054839290613d8d908490615ecd565b90915550506001600160a01b039092166000908152602081815260408083209383529290522080549091019055565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612dda848261512c565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16611b8457604051631afcd79f60e31b815260040160405180910390fd5b612174613e0d565b60008561ffff168761ffff161180613e805750610fff8661ffff16115b80613e9057506127108561ffff16115b80613ea057506109c48361ffff16115b80613eb25750620fffff8262ffffff16115b15613ed057604051631c07203f60e01b815260040160405180910390fd5b605c82901b6dfffff00000000000000000000000166dffffffffffffffffffffffffffff1991821661ffff8a1690811760108a901b630fff000016908117601c8a901b64fff00000001690811760288a901b663fff00000000001690811760368a901b693fffffc000000000000016908117604e8a901b6b0fffc000000000000000000016908117871797949095179092171717909117909117908a16175b9998505050505050505050565b6000620fffff8262ffffff161115613fa757604051631c07203f60e01b815260040160405180910390fd5b700fffff0000000000000000000000000000198316700fffff0000000000000000000000000000607084901b1617611586565b600080613fe684612f49565b62ffffff16905080156131c85760008361ffff1661400386612c52565b62ffffff16029050606460638280028402016131c3565b60008161ffff1661402a84612ebc565b61ffff16026402540be40002905092915050565b60008060008360000361405a5750600160801b91506107359050565b5082600081121561406c579015906000035b6210000081101561425c57600160801b9250846001600160801b0381111561409657911591600019045b60018216156140a75792830260801c925b800260801c60028216156140bd5792830260801c925b800260801c60048216156140d35792830260801c925b800260801c60088216156140e95792830260801c925b800260801c60108216156140ff5792830260801c925b800260801c60208216156141155792830260801c925b800260801c604082161561412b5792830260801c925b8002608090811c908216156141425792830260801c925b800260801c6101008216156141595792830260801c925b800260801c6102008216156141705792830260801c925b800260801c6104008216156141875792830260801c925b800260801c61080082161561419e5792830260801c925b800260801c6110008216156141b55792830260801c925b800260801c6120008216156141cc5792830260801c925b800260801c6140008216156141e35792830260801c925b800260801c6180008216156141fa5792830260801c925b800260801c620100008216156142125792830260801c925b800260801c6202000082161561422a5792830260801c925b800260801c620400008216156142425792830260801c925b800260801c6208000082161561425a5792830260801c925b505b8260000361428757604051631dba598d60e11b81526004810186905260248101859052604401611802565b81614292578261277c565b61277c83600019615f60565b6000620fffff8262ffffff1611156142c957604051631c07203f60e01b815260040160405180910390fd5b7fffffffffffffffffffffffffff00000fffffffffffffffffffffffffffffffff831672fffff000000000000000000000000000000000608484901b1617611586565b60008061431883614711565b90507fffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffffff84167affffffffff0000000000000000000000000000000000000000000060b083901b16176122b3565b806001600160801b03811681146133bf57604051632f45db3d60e21b815260040160405180910390fd5b60008161439b81614bbc565b670de0b6b3a7640000670de0b6b3a763ffff6001600160801b03868116908616020104949350505050565b600060ff831684811b9061ffff6101008290031686901c90612c489087906001901b86858561519c565b60008060006143ff8686615248565b9150915081600014614415578360ff1682901c92505b801561445557600160ff85161b811061444157604051638e471a8960e01b815260040160405180910390fd5b8360ff166101000361ffff1681901b830192505b50509392505050565b60006001600160801b038316608084901c61277c828286615267565b60008061ffff600884901c1660ff8085169081146144cf57600082815260028701602052604081205493506144af84836152d2565b905060001981146144cd5760089290921b9091179250610735915050565b505b5060ff80821690600883901c90821461454957600081815260018801602052604081205494506144ff85846152d2565b9050600019811461454757600882901b8117600081815260028a0160205260409020549550935061452f856152e1565b60ff1660088560001c901b1795505050505050610735565b505b60ff818116925082146145b95786549350600061456685846152d2565b905060001981146145b75760008181526001890160205260409020549450905080614590856152e1565b60ff16600883901b17600081815260028a0160205260409020549550935061452f856152e1565b505b5060009695505050505050565b60008061ffff600884901c1660ff8416801561461957600082815260028701602052604081205493506145f9848361537d565b905060001981146146175760089290921b9091179250610735915050565b505b5060ff8116600882901c81156146785760008181526001880160205260408120549450614646858461537d565b9050600019811461467657600882901b8117600081815260028a0160205260409020549550935061452f8561538d565b505b60ff8116915081156146e757865493506000614694858461537d565b905060001981146146e557600081815260018901602052604090205494509050806146be8561538d565b60ff16600883901b17600081815260028a0160205260409020549550935061452f8561538d565b505b5062ffffff9695505050505050565b600060d882901c610735565b60006107358260d01c60ff1690565b8064ffffffffff811681146133bf576040516350dbfc8760e11b815260040160405180910390fd5b6000806147458461286d565b90508062ffffff168362ffffff1611614760578281036122b3565b90910392915050565b64ffffffffff841662ffffff84811682029184821681029184160261478d886148c4565b6147979084615c86565b92506147a2886148da565b6147ac9083615c86565b91506147b7886148f0565b6147c19082615c86565b9050955095509592505050565b60d082901b7aff000000000000000000000000000000000000000000000000000016609084901b79ffffffffffffffff00000000000000000000000000000000000016605086901b71ffffffffffffffff0000000000000000000016601088901b69ffffffffffffffff00001661ffff8a161717171760d882901b7fffffffffff00000000000000000000000000000000000000000000000000000016811761299b565b8161487c8161489f565b818461ffff6000198601811690811061489757614897615bdd565b015550505050565b8061ffff166000036117c55760405163944e113960e01b815260040160405180910390fd5b60006107358260101c67ffffffffffffffff1690565b60006107358260501c67ffffffffffffffff1690565b60006107358260901c67ffffffffffffffff1690565b6000808080614916600186615f74565b61ffff16905060008060008961ffff1690505b8385116149d4576000600161493e8688615ecd565b901c9050888183089a508b61ffff808d1690811061495e5761495e615bdd565b0154935061496b84612ec8565b92508964ffffffffff168364ffffffffff1611156149955761498e600182615ee0565b94506149ce565b8964ffffffffff168364ffffffffff1610156149bd576149b6816001615ecd565b95506149ce565b838497509750505050505050614a4f565b50614929565b8164ffffffffff168964ffffffffff161015614a25578961ffff166000036149fa578799505b8a61ffff6000198c018116908110614a1457614a14615bdd565b015483965096505050505050614a4f565b8760018b089950828b61ffff808d16908110614a4357614a43615bdd565b01549650965050505050505b94509492505050565b600080600080614a67886148c4565b67ffffffffffffffff1690506000614a7e896148da565b67ffffffffffffffff1690506000614a958a6148f0565b67ffffffffffffffff1690508664ffffffffff16600003614abc5791945092509050614bb2565b6000614ac78a6148c4565b67ffffffffffffffff1690506000614ade8b6148da565b67ffffffffffffffff1690506000614af58c6148f0565b67ffffffffffffffff1690508a64ffffffffff16600003614b205791975095509350614bb292505050565b6000614b3664ffffffffff808d16908e16615ecd565b9050808b64ffffffffff1685028d64ffffffffff1689020181614b5b57614b5b615c10565b049950808b64ffffffffff1684028d64ffffffffff1688020181614b8157614b81615c10565b049850808b64ffffffffff1683028d64ffffffffff1687020181614ba757614ba7615c10565b049750505050505050505b9450945094915050565b67016345785d8a0000816001600160801b031611156117c557604051638630bc6b60e01b815260040160405180910390fd5b6000806000614bfd8686615248565b91509150612c48868686858561519c565b600081600103614c215750607f19919050565b81600003614c425760405163125946cf60e11b815260040160405180910390fd5b60019190911c9060006f800000000000000000000000000000008310614c6a57506001614c85565b5060001982600160fe1b81614c8157614c81615c10565b0492505b6000614c94607f85901c61538d565b707f80000000000000000000000000000000607f82901b16935060ff16905083811c6f800000000000000000000000000000008114614d10576f400000000000000000000000000000005b6000811315614d0e57908002607f1c90600160801b8210614d06579384019360019190911c905b60011d614cdf565b505b50500260011b919050565b60008080614d338460581c67ffffffffffffffff1690565b9250614d498460181c67ffffffffffffffff1690565b91505062ffffff831672ffffffffffffffffffffffffffffffffffffff841180614d845750670de0b6b3a76400008367ffffffffffffffff16115b80614da05750670de0b6b3a76400008267ffffffffffffffff16115b1561137b57604051637752557f60e11b815260040160405180910390fd5b6000806001600160801b038516608086901c82614ddc838389615267565b905080600003614df6575060009350839250614a4f915050565b6000614e028a8961445e565b9050801580614e0f575086155b15614e2b57614e1d82615428565b899550955050505050614a4f565b614e36828883614bee565b95506000614e4587838a61549a565b905080831115614ee4576000614e5b8285615ee0565b9050600160801b8110614e9c57608081901c858111614e7a5780614e7c565b855b9050614e888187615ee0565b9550614e98608082901b83615ee0565b9150505b898110614ed0576000614eaf8b83615f60565b9050868111614ebe5780614ec0565b865b9050614ecc8188615ee0565b9650505b608085901b6001600160801b038716179a50505b7d09745258e83de0d0f4e400fce79aca44773dd596d31a6d336ca2a775b611614f16614f108d8d61243a565b8b61445e565b1115614f355760405163a2d7652760e01b815260040160405180910390fd5b50949997985050505050505050565b600081600003614f5657506000612c48565b6001600160801b038416608085901c600080614f8861075d614f788d8b61243a565b88614f83818c615ecd565b613445565b91509150836001600160801b0316826001600160801b03161115614feb576000614fce614fb58c8c613167565b614fbf8487615cc7565b6001600160801b0316906154b7565b9050614fe3816001600160801b031660801b90565b95505061502b565b826001600160801b0316816001600160801b0316111561502b57600061501e6150148c8c613167565b614fbf8588615cc7565b6001600160801b03169550505b505050509695505050505050565b8162ffffff168162ffffff161080156150555750608083901b15155b8061507a57508162ffffff168162ffffff1611801561507a57506001600160801b0383115b156109105760405163227bd87160e21b815262ffffff82166004820152602401611802565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b8117808214613553576000838152600287016020526040812082905582900361354757600883901c6000818152600180890160205260408220805460ff88169290921b821790559350839003613545578654600160ff83161b1787555060019350505050610735565b600080600052602060008351602085016000875af1905080801561516d573d80156151605760016000511483169250615167565b843b151592505b5061517d565b3d1561517d573d6000803e3d6000fd5b50806109105760405163197138bd60e11b815260040160405180910390fd5b6000816000036151bd578383816151b5576151b5615c10565b04905061277c565b8382106151dd576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000806000198385098385029250828110838203039150509250929050565b600083156152a257508083028184828161528357615283615c10565b04146152a2576040516363f1e01f60e01b815260040160405180910390fd5b82156115865760809290921b91820182811015611586576040516363f1e01f60e01b815260040160405180910390fd5b600061158683600184016154f8565b60008160801b80156152f557608091508092505b8260401b90508015615308579150604001815b8260201b9050801561531b579150602001815b8260101b9050801561532e579150601001815b8260081b90508015615341579150600801815b8260041b90508015615354579150600401815b8260021b90508015615367579150600201815b508160011b15615375576001015b60ff03919050565b6000611586836000198401615526565b60006001600160801b038211156153a65750608090811c905b67ffffffffffffffff8211156153be57604091821c91015b63ffffffff8211156153d257602091821c91015b61ffff8211156153e457601091821c91015b60ff8211156153f557600891821c91015b600f82111561540657600491821c91015b600382111561541757600291821c91015b60018211156133bf57600101919050565b60008160000361543a57506000919050565b60006154458361538d565b600181811c607f1681811b9086901c01811c80860401811c80860401811c80860401811c80860401811c80860401811c80860401901c9384900493925060ff1690508282106154945782611586565b50919050565b60006154a7848484614bee565b9050818061311e5761311e615c10565b6000816154c381614bbc565b6ec097ce7bc90715b34b9f1000000000806001600160801b03868116908616908102670de0b6b3a764000091909101026131c3565b60ff81169190911c906000821561551c5781615513846152e1565b0160ff16611586565b5060001992915050565b60ff8181031691821b91600090831561554c57806155438561538d565b60ff16036122b3565b506000199392505050565b6001600160a01b03811681146117c557600080fd5b6000806040838503121561557f57600080fd5b823561558a81615557565b946020939093013593505050565b60005b838110156155b357818101518382015260200161559b565b50506000910152565b60208152600082518060208401526155db816040850160208701615598565b601f01601f19169190910160400192915050565b803562ffffff811681146133bf57600080fd5b60006020828403121561561457600080fd5b611586826155ef565b60008083601f84011261562f57600080fd5b50813567ffffffffffffffff81111561564757600080fd5b6020830191508360208260051b8501011115612eb557600080fd5b6000806000806000806080878903121561567b57600080fd5b863561568681615557565b9550602087013561569681615557565b9450604087013567ffffffffffffffff808211156156b357600080fd5b6156bf8a838b0161561d565b909650945060608901359150808211156156d857600080fd5b506156e589828a0161561d565b979a9699509497509295939492505050565b60008083601f84011261570957600080fd5b50813567ffffffffffffffff81111561572157600080fd5b602083019150836020828501011115612eb557600080fd5b60008060006040848603121561574e57600080fd5b83359250602084013567ffffffffffffffff81111561576c57600080fd5b615778868287016156f7565b9497909650939450505050565b6000806000806060858703121561579b57600080fd5b84356157a681615557565b9350602085013567ffffffffffffffff8111156157c257600080fd5b6157ce8782880161561d565b90945092505060408501356157e281615557565b939692955090935050565b600081518084526020808501945080840160005b8381101561581d57815187529582019590820190600101615801565b509495945050505050565b83815282602082015260606040820152600061277c60608301846157ed565b803561ffff811681146133bf57600080fd5b600080600080600080600080610100898b03121561587657600080fd5b61587f89615847565b975061588d60208a01615847565b965061589b60408a01615847565b95506158a960608a01615847565b94506158b760808a016155ef565b93506158c560a08a01615847565b92506158d360c08a016155ef565b91506158e160e08a016155ef565b90509295985092959890939650565b6000806000806040858703121561590657600080fd5b843567ffffffffffffffff8082111561591e57600080fd5b61592a8883890161561d565b9096509450602087013591508082111561594357600080fd5b506159508782880161561d565b95989497509550505050565b60208152600061158660208301846157ed565b803580151581146133bf57600080fd5b6000806040838503121561599257600080fd5b61599b8361596f565b915060208301356159ab81615557565b809150509250929050565b600080600080600080600060e0888a0312156159d157600080fd5b6159da88615847565b96506159e860208901615847565b95506159f660408901615847565b9450615a0460608901615847565b9350615a12608089016155ef565b9250615a2060a08901615847565b9150615a2e60c089016155ef565b905092959891949750929550565b600060208284031215615a4e57600080fd5b813564ffffffffff8116811461158657600080fd5b60008060408385031215615a7657600080fd5b615a7f8361596f565b9150612eb2602084016155ef565b60008060408385031215615aa057600080fd5b82356001600160801b0381168114615ab757600080fd5b9150612eb26020840161596f565b600060208284031215615ad757600080fd5b5035919050565b600060208284031215615af057600080fd5b61158682615847565b60008060408385031215615b0c57600080fd5b8235615ab781615557565b60008060408385031215615b2a57600080fd5b823561599b81615557565b60008060008060608587031215615b4b57600080fd5b8435615b5681615557565b935060208501359250604085013567ffffffffffffffff811115615b7957600080fd5b615950878288016156f7565b600060208284031215615b9757600080fd5b815161158681615557565b634e487b7160e01b600052604160045260246000fd5b604081526000615bcb60408301856157ed565b828103602084015261277c81856157ed565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615c0557600080fd5b813561158681615557565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff8281168282160390808211156131c8576131c8615c26565b67ffffffffffffffff818116838216028082169190828114615c7e57615c7e615c26565b505092915050565b67ffffffffffffffff8181168382160190808211156131c8576131c8615c26565b6001600160801b038181168382160190808211156131c8576131c8615c26565b6001600160801b038281168282160390808211156131c8576131c8615c26565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115615d1957600080fd5b8260051b80836020870137939093016020019392505050565b604081526000615d46604083018688615ce7565b828103602084015261299b818587615ce7565b604081526000615d6d604083018587615ce7565b8281036020840152612c4881856157ed565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006001600160a01b03808a168352808916602084015280881660408401525085606083015284608083015260c060a0830152613f6f60c083018486615d7f565b60008251615dfb818460208701615598565b9190910192915050565b600060208284031215615e1757600080fd5b5051919050565b60006001600160a01b03808a168352808916602084015280881660408401525060a06060830152615e5360a083018688615ce7565b8281036080840152615e66818587615ce7565b9a9950505050505050505050565b83815260406020820152600061277c604083018486615d7f565b60006001600160a01b03808816835280871660208401525060806040830152615ebb608083018587615ce7565b90508260608301529695505050505050565b8082018082111561073557610735615c26565b8181038181111561073557610735615c26565b64ffffffffff8181168382160190808211156131c8576131c8615c26565b600061ffff80841680615f2657615f26615c10565b92169190910692915050565b600082615f4157615f41615c10565b600160ff1b821460001984141615615f5b57615f5b615c26565b500590565b600082615f6f57615f6f615c10565b500490565b61ffff8281168282160390808211156131c8576131c8615c2656fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122021d0d6a98b619f239edd28c667566f18472738c97ca0f112816c25af22bc051764736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b43120c4745967fa9b93e79c149e66b0f2d6fe0c
-----Decoded View---------------
Arg [0] : factory_ (address): 0xb43120c4745967fa9b93E79C149E66B0f2D6Fe0c
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000b43120c4745967fa9b93e79c149e66b0f2d6fe0c
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MON
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.