Overview
AVAX Balance
0 AVAX
AVAX Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Loading...
Loading
Contract Name:
LBPair
Compiler Version
v0.8.10+commit.fc410830
Optimization Enabled:
Yes with 800 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import {IERC20} from "openzeppelin/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 {ILBFactory} from "./interfaces/ILBFactory.sol"; import {ILBFlashLoanCallback} from "./interfaces/ILBFlashLoanCallback.sol"; import {ILBPair} from "./interfaces/ILBPair.sol"; import {LBToken} 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 {ReentrancyGuard} from "./libraries/ReentrancyGuard.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"; /** * @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, ReentrancyGuard, 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() { if (msg.sender != address(_factory)) revert LBPair__OnlyFactory(); _; } modifier onlyProtocolFeeRecipient() { if (msg.sender != _factory.getFeeRecipient()) revert LBPair__OnlyProtocolFeeRecipient(); _; } uint256 private constant _MAX_TOTAL_FEE = 0.1e18; // 10% 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; /** * @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_; // Disable the initialize function _parameters = bytes32(uint256(1)); } /** * @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 { bytes32 parameters = _parameters; if (parameters != 0) revert LBPair__AlreadyInitialized(); __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 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.updateVolatilityParameters(parameters.getActiveId()); 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(); 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(); 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 nonReentrant returns (bytes32 amountsOut) { bytes32 reserves = _reserves; bytes32 protocolFees = _protocolFees; bytes32 amountsLeft = swapForY ? reserves.receivedX(_tokenX()) : reserves.receivedY(_tokenY()); if (amountsLeft == 0) revert LBPair__InsufficientAmountIn(); reserves = reserves.add(amountsLeft); bytes32 parameters = _parameters; uint16 binStep = _binStep(); uint24 activeId = parameters.getActiveId(); parameters = parameters.updateReferences(); 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); } } /** * @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 nonReentrant { if (amounts == 0) revert LBPair__ZeroBorrowAmount(); bytes32 reservesBefore = _reserves; bytes32 parameters = _parameters; bytes32 totalFees = _getFlashLoanFees(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(); totalFees = balancesAfter.sub(reservesBefore); uint24 activeId = parameters.getActiveId(); bytes32 protocolFees = totalSupply(activeId) == 0 ? totalFees : totalFees.scalarMulDivBasisPointRoundDown(parameters.getProtocolShare()); _reserves = balancesAfter; _protocolFees = _protocolFees.add(protocolFees); _bins[activeId] = _bins[activeId].add(totalFees.sub(protocolFees)); emit FlashLoan(msg.sender, receiver, activeId, amounts, totalFees, protocolFees); } /** * @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 nonReentrant notAddressZeroOrThis(to) returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted) { if (liquidityConfigs.length == 0) revert LBPair__EmptyMarketConfigs(); 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()); amountsLeft = _mintBins(liquidityConfigs, amountsReceived, to, arrays); _reserves = reserves.add(amountsReceived.sub(amountsLeft)); if (amountsLeft > 0) amountsLeft.transfer(_tokenX(), _tokenY(), refundTo); liquidityMinted = arrays.liquidityMinted; emit TransferBatch(msg.sender, address(0), to, arrays.ids, liquidityMinted); emit DepositedToBins(msg.sender, to, arrays.ids, arrays.amounts); } /** * @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 nonReentrant checkApproval(from, msg.sender) returns (bytes32[] memory amounts) { if (ids.length == 0 || ids.length != amountsToBurn.length) revert LBPair__InvalidInput(); 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); amountsOut.transfer(_tokenX(), _tokenY(), to); emit TransferBatch(msg.sender, from, address(0), ids, amountsToBurn); emit WithdrawnFromBins(msg.sender, to, ids, amounts); } /** * @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); collectedProtocolFees.transfer(_tokenX(), _tokenY(), msg.sender); emit CollectedProtocolFees(msg.sender, collectedProtocolFees); } } /** * @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 { 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 onlyFactory { _setStaticFeeParameters( _parameters, baseFactor, filterPeriod, decayPeriod, reductionFactor, variableFeeControl, protocolShare, maxVolatilityAccumulator ); } /** * @notice Forces the decay of the volatility reference variables * @dev Can only be called by the factory */ function forceDecay() external override onlyFactory { bytes32 parameters = _parameters; _parameters = parameters.updateIdReference().updateVolatilityReference(); emit ForcedDecay(msg.sender, parameters.getIdReference(), parameters.getVolatilityReference()); } /** * @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 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); bytes32 fees = binReserves.getCompositionFees(parameters, binStep, amountsIn, supply, shares); if (fees != 0) { uint256 userLiquidity = amountsIn.sub(fees).getLiquidity(price); uint256 binLiquidity = binReserves.getLiquidity(price); shares = userLiquidity.mulDivRoundDown(supply, binLiquidity); bytes32 protocolCFees = fees.scalarMulDivBasisPointRoundDown(parameters.getProtocolShare()); if (protocolCFees != 0) { amountsInToBin = amountsInToBin.sub(protocolCFees); _protocolFees = _protocolFees.add(protocolCFees); } 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 // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ 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 amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` 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 amount) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.10; 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) { if (account == address(0) || account == address(this)) revert LBToken__AddressThisOrZero(); _; } /** * @dev Modifier to check if the length of the arrays are equal. */ modifier checkLength(uint256 lengthA, uint256 lengthB) { if (lengthA != lengthB) revert LBToken__InvalidLength(); _; } /** * @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); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; import {ILBPair} from "./ILBPair.sol"; import {IPendingOwnable} from "./IPendingOwnable.sol"; /** * @title Liquidity Book Factory Interface * @author Trader Joe * @notice Required interface of LBFactory contract */ interface ILBFactory is IPendingOwnable { 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(); /** * @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 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/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/token/ERC20/IERC20.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__AlreadyInitialized(); 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(); 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 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 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 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 forceDecay() external; }
// 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; /** * @title Liquidity Book Pending Ownable Interface * @author Trader Joe * @notice Required interface of Pending Ownable contract used for LBFactory */ interface IPendingOwnable { error PendingOwnable__AddressZero(); error PendingOwnable__NoPendingOwner(); error PendingOwnable__NotOwner(); error PendingOwnable__NotPendingOwner(); error PendingOwnable__PendingOwnerAlreadySet(); event PendingOwnerSet(address indexed pendingOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function owner() external view returns (address); function pendingOwner() external view returns (address); function setPendingOwner(address pendingOwner) external; function revokePendingOwner() external; function becomeOwner() external; function renounceOwnership() external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.10; /** * @title Liquidity Book Address Helper Library * @author Trader Joe * @notice This library contains functions to check if an address is a contract and * catch low level calls errors */ library AddressHelper { error AddressHelper__NonContract(); error AddressHelper__CallFailed(); /** * @notice Private view function to perform a low level call on `target` * @dev Revert if the call doesn't succeed * @param target The address of the account * @param data The data to execute on `target` * @return returnData The data returned by the call */ function callAndCatch(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returnData) = target.call(data); if (success) { if (returnData.length == 0 && !isContract(target)) revert AddressHelper__NonContract(); } else { if (returnData.length == 0) { revert AddressHelper__CallFailed(); } else { // Look for revert reason and bubble it up if present assembly { revert(add(32, returnData), mload(returnData)) } } } return returnData; } /** * @notice Private view function to return if an address is a contract * @dev It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * @param account The address of the account * @return Whether the account is a contract (true) or not (false) */ function isContract(address account) internal view returns (bool) { return account.code.length > 0; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; 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} 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(); /** * @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 (totalSupply == 0 || userLiquidity == 0) return (userLiquidity, amountsIn); uint256 binLiquidity = getLiquidity(binReserves, price); if (binLiquidity == 0) return (userLiquidity, 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)); } 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()); } /** * @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; /** * @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; /// @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 {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) { if (fee > Constants.MAX_FEE) revert FeeHelper__FeeTooLarge(); _; } /** * @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); } } }
// 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) { if (oracleId == 0) revert OracleHelper__InvalidOracleId(); _; } /** * @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), activeId, 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))); } }
// 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 * @return newParams The updated encoded pair parameters */ function updateTimeOfLastUpdate(bytes32 params) internal view returns (bytes32 newParams) { uint40 currentTime = block.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 * @return The updated encoded pair parameters */ function updateReferences(bytes32 params) internal view returns (bytes32) { uint256 dt = block.timestamp - getTimeOfLastUpdate(params); if (dt >= getFilterPeriod(params)) { params = updateIdReference(params); params = dt < getDecayPeriod(params) ? updateVolatilityReference(params) : setVolatilityReference(params, 0); } return updateTimeOfLastUpdate(params); } /** * @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 * @return The updated encoded pair parameters */ function updateVolatilityParameters(bytes32 params, uint24 activeId) internal view returns (bytes32) { params = updateReferences(params); 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; /** * @title Liquidity Book Reentrancy Guard Library * @author Trader Joe * @notice This library contains functions to prevent reentrant calls to a function */ abstract contract ReentrancyGuard { error ReentrancyGuard__ReentrantCall(); /** * 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; uint256 private _status; function __ReentrancyGuard_init() internal { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal { _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() { // On the first call to nonReentrant, _notEntered will be true if (_status != _NOT_ENTERED) revert ReentrancyGuard__ReentrantCall(); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; import {AddressHelper} from "./AddressHelper.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 { using AddressHelper for address; 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); bytes memory returnData = address(token).callAndCatch(data); if (returnData.length > 0 && !abi.decode(returnData, (bool))) revert TokenHelper__TransferFailed(); } /** * @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); bytes memory returnData = address(token).callAndCatch(data); if (returnData.length > 0 && !abi.decode(returnData, (bool))) revert TokenHelper__TransferFailed(); } }
// 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 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 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 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; 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); } }
// 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; 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 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 or equal to 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 or equal to 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 {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; /** * @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; } } } }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/" ], "optimizer": { "enabled": true, "runs": 800 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract ILBFactory","name":"factory_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressHelper__CallFailed","type":"error"},{"inputs":[],"name":"AddressHelper__NonContract","type":"error"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"BinHelper__CompositionFactorFlawed","type":"error"},{"inputs":[],"name":"BinHelper__LiquidityOverflow","type":"error"},{"inputs":[],"name":"FeeHelper__FeeTooLarge","type":"error"},{"inputs":[],"name":"LBPair__AddressZero","type":"error"},{"inputs":[],"name":"LBPair__AlreadyInitialized","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__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":"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":"ReentrancyGuard__ReentrantCall","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":"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":[{"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":[{"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":"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
60a06040523480156200001157600080fd5b50604051620058a0380380620058a083398101604081905262000034916200004b565b6001600160a01b031660805260016004556200007d565b6000602082840312156200005e57600080fd5b81516001600160a01b03811681146200007657600080fd5b9392505050565b6080516157e4620000bc6000396000818161047001528181610a1601528181610f9f015281816111c5015281816118b0015261328601526157e46000f3fe608060405234801561001057600080fd5b50600436106102255760003560e01c80638d7024e51161012a578063d3b9fbe4116100bd578063e584b6541161008c578063e985e9c511610071578063e985e9c514610634578063ea3446bf14610657578063f5e293291461066a57600080fd5b8063e584b6541461060e578063e77366f81461062157600080fd5b8063d3b9fbe4146105de578063d8dfcea0146105e6578063da10610c146105ee578063dbe65edc1461060657600080fd5b8063abcd7830116100f9578063abcd78301461055b578063bd85b03914610598578063c7bd6586146105b8578063c9939f5e146105cb57600080fd5b80638d7024e5146104d257806395d89b411461050d578063a1af5b9a1461052c578063a41a01fb1461053457600080fd5b806347973bff116101bd578063551828941161018c5780637ca0de30116101715780637ca0de301461041c57806388cc58e41461046e5780638940a16a1461049457600080fd5b806355182894146103c45780636653851a1461040957600080fd5b806347973bff1461036b5780634c7cffbd1461037e5780634e1273f41461039157806353c059a0146103b157600080fd5b80630abe9688116101f95780630abe9688146102e357806317f11ecc1461031957806317fad7fc14610334578063383d15c51461034957600080fd5b8062fdd58e1461022a57806305e8746d1461025057806306fdde03146102795780630902f1ac146102bb575b600080fd5b61023d610238366004614e44565b61067d565b6040519081526020015b60405180910390f35b3660011981013560f01c90033560601c5b6040516001600160a01b039091168152602001610247565b60408051808201909152601481527f4c697175696469747920426f6f6b20546f6b656e00000000000000000000000060208201525b6040516102479190614e9c565b6102c36106ab565b604080516001600160801b03938416815292909116602083015201610247565b6102c36102f1366004614ee2565b62ffffff166000908152600760205260409020546001600160801b0381169160809190911c90565b6103216106e5565b60405161ffff9091168152602001610247565b610347610342366004614f42565b610706565b005b61035c610357366004614fd7565b61075f565b6040516102479392919061507a565b6103476103793660046150ab565b610a0b565b61023d61038c366004614ee2565b610aaa565b6103a461039f366004615142565b610ada565b60405161024791906151ae565b61023d6103bf3660046151cf565b610bc6565b6103cc610eed565b6040805160ff909616865261ffff9485166020870152939092169284019290925264ffffffffff918216606084015216608082015260a001610247565b610347610417366004615208565b610f94565b610424610ff8565b6040805161ffff9889168152968816602088015294871694860194909452918516606085015262ffffff9081166080850152931660a08301529190911660c082015260e001610247565b7f0000000000000000000000000000000000000000000000000000000000000000610261565b6104a76104a236600461528e565b611060565b6040805167ffffffffffffffff94851681529284166020840152921691810191909152606001610247565b6104da611156565b6040805162ffffff95861681529385166020850152919093169082015264ffffffffff9091166060820152608001610247565b60408051808201909152600381526213109560ea1b60208201526102ae565b61023d611196565b6105476105423660046152b5565b611362565b60405162ffffff9091168152602001610247565b61056e6105693660046152e1565b611375565b604080516001600160801b0394851681529284166020840152921691810191909152606001610247565b61023d6105a636600461531b565b60009081526001602052604090205490565b6103476105c6366004615334565b611516565b6103a46105d9366004614f42565b61158a565b6103476118a5565b6102c361195b565b3660011981013560f01c90036014013560601c610261565b610547611976565b61034761061c36600461534f565b611983565b61056e61062f3660046152e1565b611992565b61064761064236600461536d565b611ad3565b6040519015158152602001610247565b61034761066536600461538b565b611adf565b61054761067836600461531b565b611e3f565b6001600160a01b0382166000908152602081815260408083208484529091529020545b92915050565b905090565b6000806106dc6106c8600654600554611e6a90919063ffffffff16565b6001600160801b0381169160809190911c90565b90939092509050565b60006106a660003660011981013560f090811c90910360280135901c905090565b85336107128282611ea4565b6107475760405163548f773d60e01b81526001600160a01b038084166004830152821660248201526044015b60405180910390fd5b610755888888888888611ef0565b5050505050505050565b600080606060016003541461078757604051635db5c7cd60e11b815260040160405180910390fd5b6002600355866001600160a01b03811615806107ab57506001600160a01b03811630145b156107c9576040516345c210e760e11b815260040160405180910390fd5b856107e75760405163296db0d960e01b815260040160405180910390fd5b600060405180606001604052808989905067ffffffffffffffff81111561081057610810615414565b604051908082528060200260200182016040528015610839578160200160208202803683370190505b5081526020018867ffffffffffffffff81111561085857610858615414565b604051908082528060200260200182016040528015610881578160200160208202803683370190505b5081526020018867ffffffffffffffff8111156108a0576108a0615414565b6040519080825280602002602001820160405280156108c9578160200160208202803683370190505b5090526005549091506108f5813660011981013560f01c90038035606090811c9160140135901c61209c565b95506109048989888d866120dc565b945061091a6109138787611e6a565b8290612211565b600555841561094757610947853660011981013560f01c90038035606090811c9160140135901c8a61224b565b81604001519350896001600160a01b031660006001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8560000151886040516109a292919061542a565b60405180910390a4815160208301516040516001600160a01b038d169233927f87f1f9dcf5e8089a3e00811b6a008d8f30293a3da878cb1fe8c90ca376402f8a926109ee92919061542a565b60405180910390a350505060016003819055509450945094915050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a5457604051632486085360e01b815260040160405180910390fd5b6004548015610a76576040516359e270a560e01b815260040160405180910390fd5b610a7e6122b1565b610a9f610a93610a8e83856122bd565b61230c565b8a8a8a8a8a8a8a612360565b505050505050505050565b60006106a0610ace60003660011981013560f090811c90910360280135901c905090565b62ffffff8416906124e7565b60608382808214610afe576040516340311ffd60e11b815260040160405180910390fd5b8567ffffffffffffffff811115610b1757610b17615414565b604051908082528060200260200182016040528015610b40578160200160208202803683370190505b50925060005b86811015610bbb57610b96888883818110610b6357610b6361544f565b9050602002016020810190610b789190615465565b878784818110610b8a57610b8a61544f565b9050602002013561067d565b848281518110610ba857610ba861544f565b6020908102919091010152600101610b46565b505050949350505050565b6000600160035414610beb57604051635db5c7cd60e11b815260040160405180910390fd5b6002600355600554600654600085610c1f57610c1a3660011981013560f01c90036014013560601c849061251e565b610c39565b610c393660011981013560f01c90033560601c8490612553565b905080610c5957604051637df801c760e01b815260040160405180910390fd5b610c638382612211565b6004549093506000610c8a60003660011981013560f090811c90910360280135901c905090565b90506000610c9783612582565b9050610ca28361258e565b92505b62ffffff8116600090815260076020526040902054610cc5818b15612603565b610df757610cd38483612632565b93506000806000610cf587878f888c896126ba9095949392919063ffffffff16565b919450925090508215610df357610d0c8884611e6a565b9750610d188b83612211565b9a506000610d33610d2889612882565b839061ffff16612892565b90508015610d5457610d458a82612211565b9950610d518482611e6a565b93505b610d6883610d628787612211565b90611e6a565b62ffffff87166000908152600760205260409020556001600160a01b038d16337fad7d6f97abf51ce18e17a38f4d70e975be9c0708474987bb3e26ad21bd93ca70888787610db58e612905565b6040805162ffffff9586168152602081019490945283019190915290911660608201526080810186905260a0810185905260c00160405180910390a3505b5050505b84610e025750610e4c565b6000610e0e8b84612916565b905062ffffff81161580610e26575062ffffff818116145b15610e4457604051631a6d7fb160e31b815260040160405180910390fd5b915050610ca5565b86610e6a5760405163439df85d60e11b815260040160405180910390fd5b610e748688611e6a565b6005556006859055610e88600b8483612938565b9250610e9483826122bd565b6004558815610ec057610ebb3660011981013560f01c90036014013560601c88908a612a48565b610edb565b610edb3660011981013560f01c90033560601c88908a612a88565b50506001600355509295945050505050565b60045460789060009081908190819081610f0682612a99565b905061ffff811615610f8b576000610f1f600b83612aa9565b96509050610f2c81612b07565b9650610f3781612b13565b945064ffffffffff8516610f4a57600095505b61ffff861615610f8957610f7b8661ffff168361ffff1681610f6e57610f6e615482565b600b916001910601612b34565b9050610f8681612b13565b93505b505b50509091929394565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610fdd57604051632486085360e01b815260040160405180910390fd5b610fef60045488888888888888612360565b50505050505050565b600080600080600080600080600454905061101281612b07565b975061101d81612b7d565b965061102881612b8d565b955061103381612b9d565b945061103e81612bad565b935061104981612882565b925061105481612bbe565b91505090919293949596565b6000806000806004549050600061107682612a99565b905061ffff8116158061108f5750428664ffffffffff16115b156110a6576000806000945094509450505061114f565b60006110b4600b8389612bcf565b91985096509450905064ffffffffff808816908216101561114b576110e26110db84612582565b8490612cf2565b5060006110ef82896154ae565b90508064ffffffffff1661110285612582565b62ffffff1661111191906154d4565b61111b9088615504565b96508064ffffffffff1661112e85612905565b62ffffff1661113d91906154d4565b6111479087615504565b9550505b5050505b9193909250565b6000806000806000600454905061116c81612905565b945061117781612d09565b935061118281612d1a565b925061118d81612d2b565b91505090919293565b60006001600354146111bb57604051635db5c7cd60e11b815260040160405180910390fd5b60026003819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634ccb20c06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611221573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112459190615530565b6001600160a01b0316336001600160a01b03161461127657604051630f8937c560e21b815260040160405180910390fd5b6006546001600160801b038116608082901c60006112d18261129957600061129c565b60015b60ff166000856001600160801b0316116112b75760006112ba565b60015b60ff169060801b6001600160801b03919091161790565b90506112dd8482611e6a565b945084156113545760068190556005546112f79086611e6a565b60055561131e853660011981013560f01c90038035606090811c9160140135901c3361224b565b60405185815233907f3f41a5ddc53701cc7db577ade4f1fca9838a8ec0b5ea50b9f0f5d17bc4554e329060200160405180910390a25b505060016003555090919050565b600061136e8383612916565b9392505050565b600454600090839082908161139f60003660011981013560f090811c90910360280135901c905090565b905060006113ac83612582565b90506113b78361258e565b92505b62ffffff81166000908152600760205260408120546113da908915612d3e565b90506001600160801b038116156114c05760006113fc62ffffff8416856124e7565b90506000876001600160801b0316836001600160801b03161161141f5782611421565b875b905061142d8685612632565b955060008a6114505761144b6001600160801b038316846080612d5e565b611465565b6114656001600160801b038316608085612d94565b905060006114738888612dca565b9050600061148a6001600160801b03841683612de9565b9050611496818461554d565b6114a0908d61554d565b9b506114ac848c61556f565b9a506114b8818b61554d565b995050505050505b6001600160801b0386166114d4575061150c565b60006114e08984612916565b905062ffffff811615806114f8575062ffffff818116145b1561150457505061150c565b9150506113ba565b5050509250925092565b600454600061152482612a99565b905061ffff81166115405750600161153c8282612e5b565b6004555b61154c600b8285612eaa565b60405161ffff8416815233907fc975541e72d695746a43ba65745d79963a23082637c8f4609354d9bcf70194d69060200160405180910390a2505050565b60606001600354146115af57604051635db5c7cd60e11b815260040160405180910390fd5b600260035586336115c08282611ea4565b6115f05760405163548f773d60e01b81526001600160a01b0380841660048301528216602482015260440161073e565b8515806115fd5750858414155b1561161b57604051633ab7870760e01b815260040160405180910390fd5b8567ffffffffffffffff81111561163457611634615414565b60405190808252806020026020018201604052801561165d578160200160208202803683370190505b5092506000805b878110156117b357600061168f8a8a848181106116835761168361544f565b90506020020135612f93565b905060008888848181106116a5576116a561544f565b90506020020135905080600014156116d757604051636996a92560e01b815262ffffff8316600482015260240161073e565b62ffffff8216600090815260076020908152604080832054600190925282205490915061170a8f8562ffffff1685612fbe565b600061171783858461303f565b90508061173e5760405163fd44792960e01b815262ffffff8616600482015260240161073e565b6117488382611e6a565b92508382141561175f5761175d6008866130b7565b505b62ffffff85166000908152600760205260409020839055895181908b908890811061178c5761178c61544f565b60209081029190910101526117a18782612211565b96508560010195505050505050611664565b506005546117c19082611e6a565b6005556117e8813660011981013560f01c90038035606090811c9160140135901c8c61224b565b60006001600160a01b03168a6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8b8b8b8b60405161183c94939291906155de565b60405180910390a4886001600160a01b0316336001600160a01b03167fa32e146844d6144a22e94c586715a1317d58a8aa3581ec33d040113ddcb243508a8a8860405161188b93929190615605565b60405180910390a350506001600355509695505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146118ee57604051632486085360e01b815260040160405180910390fd5b6004546119026118fd8261230c565b613152565b600455337f282afaeeae84c1d85ad1424a3aa2ddbedaeefca3b1e53d889d15265fe44db7fc61193083612d1a565b61193984612d09565b6040805162ffffff93841681529290911660208301520160405180910390a250565b60065460009081906001600160801b0381169060801c6106dc565b60006106a6600454612582565b61198e338383613187565b5050565b60008080806119aa6001600160801b03871686613270565b60045490915060006119d160003660011981013560f090811c90910360280135901c905090565b905060006119de83612582565b90506119e98361258e565b92505b62ffffff8116600090815260076020526040902054611a0c818a15612603565b611a7957611a1a8483612632565b935060008080611a2e8488888f898d6126ba565b919450925090508215611a7557611a458884611e6a565b9750611a52828d15612d3e565b611a5c908b61554d565b9950611a68818d612d3e565b611a72908a61554d565b98505b5050505b84611a845750611abc565b6000611a908a84612916565b905062ffffff81161580611aa8575062ffffff818116145b15611ab4575050611abc565b9150506119ec565b611ac68489612d3e565b9650505050509250925092565b600061136e8383611ea4565b600160035414611b0257604051635db5c7cd60e11b815260040160405180910390fd5b600260035582611b255760405163727ab02160e11b815260040160405180910390fd5b6005546004546000611b3686613281565b9050611b5c863660011981013560f01c90038035606090811c9160140135901c8a61224b565b6000806001600160a01b038916631faa6b8760e01b333660011981013560f01c90033560601c3660011981013560f01c90036014013560601c8c888d8d604051602401611baf979695949392919061562b565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c1a919061568f565b6000604051808303816000865af19150503d8060008114611c57576040519150601f19603f3d011682016040523d82523d6000602084013e611c5c565b606091505b5091509150811580611c7057508051602014155b80611cae57507fab5c473bce5960a8292e9c8db82f9272504caab4e9ef09553fc112f82b62a3c281806020019051810190611cab91906156ab565b14155b15611ccc57604051634a57011360e01b815260040160405180910390fd5b6000611cf1813660011981013560f01c90038035606090811c9160140135901c61209c565b9050611d07611d008786612211565b8290613389565b15611d2557604051630e3c717160e11b815260040160405180910390fd5b611d2f8187611e6a565b93506000611d3c86612582565b90506000611d5b8262ffffff1660009081526001602052604090205490565b15611d7c57611d77611d6c88612882565b879061ffff16612892565b611d7e565b855b6005849055600654909150611d939082612211565b600655611dbe611da38783611e6a565b62ffffff841660009081526007602052604090205490612211565b62ffffff83166000818152600760209081526040918290209390935580519182529181018d9052908101879052606081018290526001600160a01b038d169033907fd126bd9d94daca8e55ffd8283fac05394aec8326c6b1639e1e8a445fbe8bbc7d9060800160405180910390a35050600160035550505050505050505050565b60006106a0611e6360003660011981013560f090811c90910360280135901c905090565b83906133cd565b80820382811180611e8657506001600160801b03808416908216115b156106a05760405163e599af5560e01b815260040160405180910390fd5b6000816001600160a01b0316836001600160a01b0316148061136e5750506001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b8281808214611f12576040516340311ffd60e11b815260040160405180910390fd5b866001600160a01b0381161580611f3157506001600160a01b03811630145b15611f4f576040516345c210e760e11b815260040160405180910390fd5b6001600160a01b03808a16600090815260208190526040808220928b1682528120905b888110156120335760008a8a83818110611f8e57611f8e61544f565b9050602002013590506000898984818110611fab57611fab61544f565b600085815260208981526040909120549102929092013592505081811015612008578e83836040516358b4690f60e01b815260040161073e939291906001600160a01b039390931683526020830191909152604082015260600190565b6000928352602086815260408085209284900390925585905290912080549091019055600101611f72565b50896001600160a01b03168b6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8c8c8c8c60405161208794939291906155de565b60405180910390a45050505050505050505050565b60006120d484610d626120ae85613412565b6120b787613412565b6001600160801b03169060801b6001600160801b03919091161790565b949350505050565b6000806120fe60003660011981013560f090811c90910360280135901c905090565b600454909150600061210f82612582565b905086935060005b888110156122045760008061214d8a8d8d868181106121385761213861544f565b9050602002013561348190919063ffffffff16565b915091506000806000612163898886888c6134ca565b919450925090506121748a83611e6a565b99508362ffffff168b6000015187815181106121925761219261544f565b602002602001018181525050808b6020015187815181106121b5576121b561544f565b602002602001018181525050828b6040015187815181106121d8576121d861544f565b6020026020010181815250506121f48c8562ffffff16856136c7565b8560010195505050505050612117565b5050505095945050505050565b8181018281108061222d57506001600160801b03808416908216105b156106a0576040516316692de960e01b815260040160405180910390fd5b6001600160801b038416608085901c811561227d5761227d6001600160a01b038616846001600160801b038516613714565b6001600160801b038116156122a9576122a96001600160a01b038516846001600160801b038416613714565b505050505050565b6122bb6001600355565b565b60007cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83167fffffff000000000000000000000000000000000000000000000000000000000060e884901b161761136e565b60008061231883612582565b90507fffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffff831675ffffff00000000000000000000000000000000000000609883901b161761136e565b61ffff8716158015612374575061ffff8616155b8015612382575061ffff8516155b8015612390575061ffff8416155b801561239f575062ffffff8316155b80156123ad575061ffff8216155b80156123bc575062ffffff8116155b156123da576040516331375e0360e11b815260040160405180910390fd5b6123ea88888888888888886137ca565b9750600061240d60003660011981013560f090811c90910360280135901c905090565b9050600061241b8a846138ca565b905060006124298284613928565b6124338385613968565b61243d91906156c4565b905067016345785d8a0000811115612468576040516372db9c0b60e11b815260040160405180910390fd5b50505060048890556040805161ffff8981168252888116602083015287811682840152868116606083015262ffffff868116608084015290851660a0830152831660c0820152905133917fd09e5ddc721ff14c5c1e66a305cbba1fd70b82c5232bc391aad6f55e62e4b046919081900360e00190a25050505050505050565b600061271061ffff60801b608084901b1604600160801b0162ffffff8416627fffff1901612515828261398c565b95945050505050565b60008061252b8460801c90565b90506120d48161253a85613412565b612544919061556f565b6001600160801b031660801b90565b60006001600160801b0383166120d48161256c85613412565b612576919061556f565b6001600160801b031690565b600060e882901c6106a0565b60008061259a83612d2b565b6125ab9064ffffffffff16426156dc565b90506125b683612b7d565b61ffff1681106125fa576125c98361230c565b92506125d483612b8d565b61ffff1681106125ee576125e9836000613be5565b6125f7565b6125f783613152565b92505b61136e83613c53565b600081612623576126148360801c90565b6001600160801b03161561136e565b50506001600160801b03161590565b60008061263e84612d1a565b62ffffff169050600080828562ffffff1611612661578462ffffff16830361266a565b828562ffffff16035b9150612710820261267a87612d09565b62ffffff16019050600061268d87612bbe565b62ffffff1690508082116126a157816126a3565b805b91506126af87836138ca565b979650505050505050565b60008080806126ce62ffffff8716896124e7565b905060006126dd8b8915612d3e565b9050600088612708576127036126fe6001600160801b038416856080612d5e565b613cac565b612720565b6127206126fe6001600160801b038416608086612d94565b9050600061272e8c8c612dca565b905060006127456001600160801b03841683612de9565b9050612751818461554d565b9250600061275f8a8d612d3e565b9050600080856001600160801b0316836001600160801b03161061278a575084915082905085612800565b61279d6001600160801b03841686613cd6565b915060006127ab838561556f565b6001600160801b031690508e6127cf576127ca6126fe8260808c613d36565b6127de565b6127de6126fe828b6080613d60565b9150876001600160801b0316826001600160801b031611156127fe578791505b505b8d61283a57612818836001600160801b031660801b90565b6001600160801b038216612835846001600160801b031660801b90565b612862565b6001600160801b038316612857826001600160801b031660801b90565b6001600160801b0384165b809b50819c50829d50505050505050505050505096509650969350505050565b60006106a082604e1c613fff1690565b60006001600160801b0382166128aa575060006106a0565b6127106001600160801b0383168110156128d757604051631514a94960e21b815260040160405180910390fd5b6001600160801b03848116840282900490608086811c86028490049183169082901b175b9695505050505050565b60006106a08260701c620fffff1690565b60008261292d57612928600883613dce565b61136e565b61136e600883613f1a565b60008061294484612a99565b905061ffff8116612958578391505061136e565b60006129648683612b34565b905060006129718261404a565b9050600061297e83614056565b61298b9060ff16836156f3565b90508064ffffffffff1661299e42614065565b64ffffffffff161115612a3c5760008060006129db8464ffffffffff1642038a6129c78d612905565b6129d18e8e61408d565b8a939291906140bd565b92509250925060006129ec87612b07565b905064ffffffffff861642036078811115612a1c575096879006600101964295506000612a198c8a612e5b565b9b505b612a2a82868686858c614122565b97505050505050612a3c8885856141c6565b50949695505050505050565b6000612a548460801c90565b90506001600160801b03811615612a8257612a826001600160a01b038416836001600160801b038416613714565b50505050565b60006001600160801b038416612a54565b60006106a08260d81c61ffff1690565b600080612ab68484612b34565b9150612ac182612b07565b90508061ffff168361ffff1614612b0057612ae4612adf8583612b34565b612b07565b90508061ffff168361ffff1611612afb5780612afd565b825b90505b9250929050565b600061ffff82166106a0565b6000612b1e82614056565b60ff16612b2a8361404a565b6106a091906156f3565b60008161ffff8116612b595760405163944e113960e01b815260040160405180910390fd5b8361ffff60001985018116908110612b7357612b7361544f565b0154949350505050565b60006106a08260101c610fff1690565b60006106a082601c1c610fff1690565b60006106a08260281c613fff1690565b60006106a08260361c62ffffff1690565b60006106a082605c1c620fffff1690565b600080600080600080612be28989612aa9565b909250905064ffffffffff8716612c198a612bfd848c615713565b61ffff1661ffff8110612c1257612c1261544f565b0154612b13565b64ffffffffff161115612c3f57604051631c4e7f9560e21b815260040160405180910390fd5b612c4882612b13565b95508664ffffffffff168664ffffffffff1611612c8a5785612c698361420c565b612c7284614222565b612c7b85614238565b95509550955095505050612ce9565b869550600080612c9c8b8b8b8661424e565b91509150600089612cac83612b13565b612cb691906154ae565b90506000612cc384612b13565b612ccd908c6154ae565b9050612cdb8484848461439d565b919a50985096505050505050505b93509350935093565b6000612cfd8361258e565b925061136e8383612632565b60006106a08260841c620fffff1690565b60006106a08260981c62ffffff1690565b60006106a08260b01c64ffffffffff1690565b600081612d4f576129288360801c90565b6001600160801b03831661136e565b6000612d6b848484613d60565b9050600160ff83161b80612d8157612d81615482565b8385091561136e576120d46001826156c4565b6000612da1848484613d36565b90508180612db157612db1615482565b600160ff85161b85091561136e576120d46001826156c4565b600061136e612dd98484613928565b612de38585613968565b01613cac565b60008167016345785d8a0000816001600160801b03161115612e1e57604051638630bc6b60e01b815260040160405180910390fd5b6001600160801b03808416670de0b6b3a76400008190039182916000199188160282010181612e4f57612e4f615482565b049250505b5092915050565b60007fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff83167cffff00000000000000000000000000000000000000000000000000000060d884901b161761136e565b6000612eb68484612b34565b90506000612ec382612b07565b90508261ffff168161ffff1610612eed57604051632c57574160e21b815260040160405180910390fd5b60008461ffff168261ffff1614612f1e5761ffff821615612f1757612f128683612b34565b612f20565b6000612f20565b825b90506000612f2d82612b07565b61ffff169050808661ffff1611612f445780612f4a565b8561ffff165b905061ffff83165b8561ffff16811015612f7c5781888261ffff8110612f7257612f7261544f565b0155600101612f52565b50610fef878761ffff8681168818908916176141c6565b8062ffffff81168114612fb957604051639b63641560e01b815260040160405180910390fd5b919050565b6001600160a01b03831660009081526020818152604080832085845291829052909120548281101561301b57604051625b387760e31b81526001600160a01b0386166004820152602481018590526044810184905260640161073e565b60009384526001602090815260408086208054869003905592905292209103905550565b60006001600160801b038416608085901c828083156130725761306f6126fe886001600160801b038716896144fb565b91505b6001600160801b0383161561309b576130986126fe886001600160801b038616896144fb565b90505b608081901b6001600160801b0383161798975050505050505050565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b19811680821461314657600083815260028701602052604090208190558061313a57600883901c600081815260018089016020526040909120805460ff87169290921b1982169055925082613138578654600160ff83161b191687555b505b600193505050506106a0565b50600095945050505050565b60008061315e83612905565b62ffffff169050600061317084612b9d565b61ffff169050612710828202046125158582613be5565b826001600160a01b03811615806131a657506001600160a01b03811630145b156131c4576040516345c210e760e11b815260040160405180910390fd5b826001600160a01b0316846001600160a01b031614156132025760405163782ee70760e01b81526001600160a01b038516600482015260240161073e565b6001600160a01b03848116600081815260026020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b600081612d4f576129288360801b90565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fd90c2be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061330691906156ab565b90506001600160801b0383811690608085901c90670de0b6b3a763ffff9061333f90670de0b6b3a7640000908616850283015b04613cac565b9250613371670de0b6b3a764000082866001600160801b0316856001600160801b031602018161333957613339615482565b915050608081901b6001600160801b03831617612515565b60006001600160801b0383811690608085811c9185169085901c818410806126af5750806001600160801b0316836001600160801b03161094505050505092915050565b600061271061ffff60801b608084901b1604600160801b01816133ef8261451b565b6133f88661451b565b6134029190615734565b9050612515816280000001612f93565b6040516370a0823160e01b81523060048201526000906106a0906001600160a01b038416906370a0823190602401602060405180830381865afa15801561345d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126fe91906156ab565b600080600080600061349287614626565b670de0b6b3a764000060808a811c90930281900490921b6001600160801b03998a169093029190910490971617979596505050505050565b62ffffff831660008181526007602052604081205490918291829182906134f1908b6124e7565b905060006135108962ffffff1660009081526001602052604090205490565b905061351e838984846146c9565b909650945084935062ffffff898116908b16141561364857613540878a612cf2565b9650600061355284898e89868c6147ef565b905080156136425760006135708461356a8985611e6a565b906148e1565b9050600061357e86866148e1565b905061358b8285836144fb565b985060006135a661359b8c612882565b859061ffff16612892565b905080156135cc576135b88882611e6a565b6006549098506135c89082612211565b6006555b6135d8600b8c8f612938565b9a508a600481905550336001600160a01b03167f3f0b46725027bb418b2005f4683538eccdbcdf1de2b8649a29dbd9c507d16ff48e86846040516136369392919062ffffff9390931683526020830191909152604082015260600190565b60405180910390a25050505b50613653565b613653858b8b6148fd565b85158061365e575083155b1561368357604051634c98d35760e11b815262ffffff8a16600482015260240161073e565b806136955761369360088a614968565b505b61369f8385612211565b62ffffff90991660009081526007602052604090209890985550929891975095509350505050565b600082815260016020526040812080548392906136e59084906156c4565b90915550506001600160a01b039092166000908152602081815260408083209383529290522080549091019055565b604080516001600160a01b03848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b17905290600090613781908616836149f2565b9050600081511180156137a55750808060200190518101906137a39190615762565b155b156137c35760405163197138bd60e11b815260040160405180910390fd5b5050505050565b60008561ffff168761ffff1611806137e75750610fff8661ffff16115b806137f757506127108561ffff16115b8061380757506109c48361ffff16115b806138195750620fffff8262ffffff16115b1561383757604051631c07203f60e01b815260040160405180910390fd5b5060109590951b630fff00001661ffff9690961695909517601c9390931b64fff0000000169290921760289190911b663fff0000000000161760369190911b693fffffc00000000000001617604e9290921b6b0fffc00000000000000000001691909117605c9190911b6dfffff0000000000000000000000016176dffffffffffffffffffffffffffff19919091161790565b6000620fffff8262ffffff1611156138f557604051631c07203f60e01b815260040160405180910390fd5b700fffff0000000000000000000000000000198316700fffff0000000000000000000000000000607084901b161761136e565b60008061393484612bad565b62ffffff1690508015612e545760008361ffff1661395186612905565b62ffffff1602905060646063828002840201612e4f565b60008161ffff1661397884612b07565b61ffff16026402540be40002905092915050565b60008080836139a45750600160801b91506106a09050565b508260008112156139b6579015906000035b62100000811015613ba657600160801b9250846001600160801b038111156139e057911591600019045b60018216156139f15792830260801c925b800260801c6002821615613a075792830260801c925b800260801c6004821615613a1d5792830260801c925b800260801c6008821615613a335792830260801c925b800260801c6010821615613a495792830260801c925b800260801c6020821615613a5f5792830260801c925b800260801c6040821615613a755792830260801c925b8002608090811c90821615613a8c5792830260801c925b800260801c610100821615613aa35792830260801c925b800260801c610200821615613aba5792830260801c925b800260801c610400821615613ad15792830260801c925b800260801c610800821615613ae85792830260801c925b800260801c611000821615613aff5792830260801c925b800260801c612000821615613b165792830260801c925b800260801c614000821615613b2d5792830260801c925b800260801c618000821615613b445792830260801c925b800260801c62010000821615613b5c5792830260801c925b800260801c62020000821615613b745792830260801c925b800260801c62040000821615613b8c5792830260801c925b800260801c62080000821615613ba45792830260801c925b505b82613bce57604051631dba598d60e11b8152600481018690526024810185905260440161073e565b81613bd95782612515565b6125158360001961577f565b6000620fffff8262ffffff161115613c1057604051631c07203f60e01b815260040160405180910390fd5b7fffffffffffffffffffffffffff00000fffffffffffffffffffffffffffffffff831672fffff000000000000000000000000000000000608484901b161761136e565b600080613c5f42614065565b90507fffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffffff83167affffffffff0000000000000000000000000000000000000000000060b083901b161761136e565b806001600160801b0381168114612fb957604051632f45db3d60e21b815260040160405180910390fd5b60008167016345785d8a0000816001600160801b03161115613d0b57604051638630bc6b60e01b815260040160405180910390fd5b670de0b6b3a7640000670de0b6b3a763ffff6001600160801b03868116908616020104949350505050565b600060ff831684811b9061ffff6101008290031686901c906128fb9087906001901b868585614abd565b6000806000613d6f8686614b66565b9150915081600014613d85578360ff1682901c92505b8015613dc557600160ff85161b8110613db157604051638e471a8960e01b815260040160405180910390fd5b8360ff166101000361ffff1681901b830192505b50509392505050565b60008061ffff600884901c1660ff808516908114613e235760008281526002870160205260408120549350613e038483614b85565b90506000198114613e215760089290921b90911792506106a0915050565b505b5060ff80821690600883901c908214613e9d5760008181526001880160205260408120549450613e538584614b85565b90506000198114613e9b57600882901b8117600081815260028a01602052604090205495509350613e8385614b94565b60ff1660088560001c901b17955050505050506106a0565b505b60ff81811692508214613f0d57865493506000613eba8584614b85565b90506000198114613f0b5760008181526001890160205260409020549450905080613ee485614b94565b60ff16600883901b17600081815260028a01602052604090205495509350613e8385614b94565b505b5060009695505050505050565b60008061ffff600884901c1660ff84168015613f6d5760008281526002870160205260408120549350613f4d8483614c30565b90506000198114613f6b5760089290921b90911792506106a0915050565b505b5060ff8116600882901c8115613fcc5760008181526001880160205260408120549450613f9a8584614c30565b90506000198114613fca57600882901b8117600081815260028a01602052604090205495509350613e8385614c40565b505b60ff81169150811561403b57865493506000613fe88584614c30565b90506000198114614039576000818152600189016020526040902054945090508061401285614c40565b60ff16600883901b17600081815260028a01602052604090205495509350613e8385614c40565b505b5062ffffff9695505050505050565b600060d882901c6106a0565b60006106a08260d01c60ff1690565b8064ffffffffff81168114612fb9576040516350dbfc8760e11b815260040160405180910390fd5b60008061409984612582565b90508062ffffff168362ffffff16116140b4578281036120d4565b90910392915050565b64ffffffffff841662ffffff8481168202918482168102918416026140e18861420c565b6140eb9084615504565b92506140f688614222565b6141009083615504565b915061410b88614238565b6141159082615504565b9050955095509592505050565b60d082901b7aff000000000000000000000000000000000000000000000000000016609084901b79ffffffffffffffff00000000000000000000000000000000000016605086901b71ffffffffffffffff0000000000000000000016601088901b69ffffffffffffffff00001661ffff8a161717171760d882901b7fffffffffff0000000000000000000000000000000000000000000000000000001681176126af565b8161ffff81166141e95760405163944e113960e01b815260040160405180910390fd5b818461ffff600019860181169081106142045761420461544f565b015550505050565b60006106a08260101c67ffffffffffffffff1690565b60006106a08260501c67ffffffffffffffff1690565b60006106a08260901c67ffffffffffffffff1690565b600080808061425e600186615793565b61ffff16905060008060008961ffff1690505b83851161431c576000600161428686886156c4565b901c9050888183089a508b61ffff808d169081106142a6576142a661544f565b015493506142b384612b13565b92508964ffffffffff168364ffffffffff1611156142dd576142d66001826156dc565b9450614316565b8964ffffffffff168364ffffffffff161015614305576142fe8160016156c4565b9550614316565b838497509750505050505050614394565b50614271565b8164ffffffffff168964ffffffffff16101561436a5761ffff8a1661433f578799505b8a61ffff6000198c0181169081106143595761435961544f565b015483965096505050505050614394565b8760018b089950828b61ffff808d169081106143885761438861544f565b01549650965050505050505b94509492505050565b6000806000806143ac8861420c565b67ffffffffffffffff16905060006143c389614222565b67ffffffffffffffff16905060006143da8a614238565b67ffffffffffffffff16905064ffffffffff87166143fe57919450925090506144f1565b60006144098a61420c565b67ffffffffffffffff16905060006144208b614222565b67ffffffffffffffff16905060006144378c614238565b67ffffffffffffffff16905064ffffffffff8b1661445f57919750955093506144f192505050565b600061447564ffffffffff808d16908e166156c4565b9050808b64ffffffffff1685028d64ffffffffff168902018161449a5761449a615482565b049950808b64ffffffffff1684028d64ffffffffff16880201816144c0576144c0615482565b049850808b64ffffffffff1683028d64ffffffffff16870201816144e6576144e6615482565b049750505050505050505b9450945094915050565b600080600061450a8686614b66565b915091506128fb8686868585614abd565b6000816001141561452f5750607f19919050565b8161454d5760405163125946cf60e11b815260040160405180910390fd5b60019190911c9060006f80000000000000000000000000000000831061457557506001614590565b5060001982600160fe1b8161458c5761458c615482565b0492505b600061459f607f85901c614c40565b707f80000000000000000000000000000000607f82901b16935060ff16905083811c6f80000000000000000000000000000000811461461b576f400000000000000000000000000000005b600081131561461957908002607f1c90600160801b8210614611579384019360019190911c905b60011d6145ea565b505b50500260011b919050565b6000808061463e8460581c67ffffffffffffffff1690565b92506146548460181c67ffffffffffffffff1690565b91505062ffffff831672ffffffffffffffffffffffffffffffffffffff84118061468f5750670de0b6b3a76400008367ffffffffffffffff16115b806146ab5750670de0b6b3a76400008267ffffffffffffffff16115b1561114f57604051637752557f60e11b815260040160405180910390fd5b6000806001600160801b038516608086901c826146e7838389614cdb565b90508515806146f4575080155b15614706579350869250614394915050565b60006147128a896148e1565b90508061472757509350869250614394915050565b6147328288836144fb565b9550600061474187838a614d46565b9050808311156147e057600061475782856156dc565b9050600160801b811061479857608081901c8581116147765780614778565b855b905061478481876156dc565b9550614794608082901b836156dc565b9150505b8981106147cc5760006147ab8b8361577f565b90508681116147ba57806147bc565b865b90506147c881886156dc565b9650505b608085901b6001600160801b038716179a50505b50949997985050505050505050565b6000816147fe575060006128fb565b6001600160801b038416608085901c6000806148306106c86148208d8b612211565b8861482b818c6156c4565b61303f565b91509150836001600160801b0316826001600160801b0316111561489357600061487661485d8c8c612dca565b614867848761556f565b6001600160801b031690614d63565b905061488b816001600160801b031660801b90565b9550506148d3565b826001600160801b0316816001600160801b031611156148d35760006148c66148bc8c8c612dca565b614867858861556f565b6001600160801b03169550505b505050509695505050505050565b60006001600160801b038316608084901c612515828286614cdb565b8162ffffff168162ffffff161080156149195750608083901b15155b8061493e57508162ffffff168162ffffff1611801561493e57506001600160801b0383115b156149635760405163227bd87160e21b815262ffffff8216600482015260240161073e565b505050565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b811780821461314657600083815260028701602052604090208190558161313a57600883901c600081815260018089016020526040909120805460ff87169290921b82179055925082613138578654600160ff83161b17875550600193505050506106a0565b6060600080846001600160a01b031684604051614a0f919061568f565b6000604051808303816000865af19150503d8060008114614a4c576040519150601f19603f3d011682016040523d82523d6000602084013e614a51565b606091505b50915091508115614a96578051158015614a7357506001600160a01b0385163b155b15614a9157604051633b99915360e21b815260040160405180910390fd5b6120d4565b8051614ab5576040516373483fab60e11b815260040160405180910390fd5b805181602001fd5b600081614adb57838381614ad357614ad3615482565b049050612515565b838210614afb576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000806000198385098385029250828110838203039150509250929050565b600061136e8360018401614dcd565b60008160801b8015614ba857608091508092505b8260401b90508015614bbb579150604001815b8260201b90508015614bce579150602001815b8260101b90508015614be1579150601001815b8260081b90508015614bf4579150600801815b8260041b90508015614c07579150600401815b8260021b90508015614c1a579150600201815b508160011b15614c28576001015b60ff03919050565b600061136e836000198401614dfb565b60006001600160801b03821115614c595750608090811c905b67ffffffffffffffff821115614c7157604091821c91015b63ffffffff821115614c8557602091821c91015b61ffff821115614c9757601091821c91015b60ff821115614ca857600891821c91015b600f821115614cb957600491821c91015b6003821115614cca57600291821c91015b6001821115612fb957600101919050565b60008315614d16575080830281848281614cf757614cf7615482565b0414614d16576040516363f1e01f60e01b815260040160405180910390fd5b821561136e5760809290921b9182018281101561136e576040516363f1e01f60e01b815260040160405180910390fd5b6000614d538484846144fb565b90508180612d8157612d81615482565b60008167016345785d8a0000816001600160801b03161115614d9857604051638630bc6b60e01b815260040160405180910390fd5b6ec097ce7bc90715b34b9f1000000000806001600160801b03868116908616908102670de0b6b3a76400009190910102612e4f565b60ff81169190911c9060008215614df15781614de884614b94565b0160ff1661136e565b5060001992915050565b60ff8181031691821b916000908315614e215780614e1885614c40565b60ff16036120d4565b506000199392505050565b6001600160a01b0381168114614e4157600080fd5b50565b60008060408385031215614e5757600080fd5b8235614e6281614e2c565b946020939093013593505050565b60005b83811015614e8b578181015183820152602001614e73565b83811115612a825750506000910152565b6020815260008251806020840152614ebb816040850160208701614e70565b601f01601f19169190910160400192915050565b803562ffffff81168114612fb957600080fd5b600060208284031215614ef457600080fd5b61136e82614ecf565b60008083601f840112614f0f57600080fd5b50813567ffffffffffffffff811115614f2757600080fd5b6020830191508360208260051b8501011115612b0057600080fd5b60008060008060008060808789031215614f5b57600080fd5b8635614f6681614e2c565b95506020870135614f7681614e2c565b9450604087013567ffffffffffffffff80821115614f9357600080fd5b614f9f8a838b01614efd565b90965094506060890135915080821115614fb857600080fd5b50614fc589828a01614efd565b979a9699509497509295939492505050565b60008060008060608587031215614fed57600080fd5b8435614ff881614e2c565b9350602085013567ffffffffffffffff81111561501457600080fd5b61502087828801614efd565b909450925050604085013561503481614e2c565b939692955090935050565b600081518084526020808501945080840160005b8381101561506f57815187529582019590820190600101615053565b509495945050505050565b838152826020820152606060408201526000612515606083018461503f565b803561ffff81168114612fb957600080fd5b600080600080600080600080610100898b0312156150c857600080fd5b6150d189615099565b97506150df60208a01615099565b96506150ed60408a01615099565b95506150fb60608a01615099565b945061510960808a01614ecf565b935061511760a08a01615099565b925061512560c08a01614ecf565b915061513360e08a01614ecf565b90509295985092959890939650565b6000806000806040858703121561515857600080fd5b843567ffffffffffffffff8082111561517057600080fd5b61517c88838901614efd565b9096509450602087013591508082111561519557600080fd5b506151a287828801614efd565b95989497509550505050565b60208152600061136e602083018461503f565b8015158114614e4157600080fd5b600080604083850312156151e257600080fd5b82356151ed816151c1565b915060208301356151fd81614e2c565b809150509250929050565b600080600080600080600060e0888a03121561522357600080fd5b61522c88615099565b965061523a60208901615099565b955061524860408901615099565b945061525660608901615099565b935061526460808901614ecf565b925061527260a08901615099565b915061528060c08901614ecf565b905092959891949750929550565b6000602082840312156152a057600080fd5b813564ffffffffff8116811461136e57600080fd5b600080604083850312156152c857600080fd5b82356152d3816151c1565b9150612afd60208401614ecf565b600080604083850312156152f457600080fd5b82356001600160801b038116811461530b57600080fd5b915060208301356151fd816151c1565b60006020828403121561532d57600080fd5b5035919050565b60006020828403121561534657600080fd5b61136e82615099565b6000806040838503121561536257600080fd5b823561530b81614e2c565b6000806040838503121561538057600080fd5b82356151ed81614e2c565b600080600080606085870312156153a157600080fd5b84356153ac81614e2c565b935060208501359250604085013567ffffffffffffffff808211156153d057600080fd5b818701915087601f8301126153e457600080fd5b8135818111156153f357600080fd5b88602082850101111561540557600080fd5b95989497505060200194505050565b634e487b7160e01b600052604160045260246000fd5b60408152600061543d604083018561503f565b8281036020840152612515818561503f565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561547757600080fd5b813561136e81614e2c565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600064ffffffffff838116908316818110156154cc576154cc615498565b039392505050565b600067ffffffffffffffff808316818516818304811182151516156154fb576154fb615498565b02949350505050565b600067ffffffffffffffff80831681851680830382111561552757615527615498565b01949350505050565b60006020828403121561554257600080fd5b815161136e81614e2c565b60006001600160801b0380831681851680830382111561552757615527615498565b60006001600160801b03838116908316818110156154cc576154cc615498565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156155c157600080fd5b8260051b8083602087013760009401602001938452509192915050565b6040815260006155f260408301868861558f565b82810360208401526126af81858761558f565b60408152600061561960408301858761558f565b82810360208401526128fb818561503f565b60006001600160a01b03808a168352808916602084015280881660408401525085606083015284608083015260c060a08301528260c0830152828460e0840137600060e0848401015260e0601f19601f850116830101905098975050505050505050565b600082516156a1818460208701614e70565b9190910192915050565b6000602082840312156156bd57600080fd5b5051919050565b600082198211156156d7576156d7615498565b500190565b6000828210156156ee576156ee615498565b500390565b600064ffffffffff80831681851680830382111561552757615527615498565b600061ffff8084168061572857615728615482565b92169190910692915050565b60008261574357615743615482565b600160ff1b82146000198414161561575d5761575d615498565b500590565b60006020828403121561577457600080fd5b815161136e816151c1565b60008261578e5761578e615482565b500490565b600061ffff838116908316818110156154cc576154cc61549856fea26469706673582212205adcacc1d216d119797176c20bff4d077411de5b9bc12719a34da1f62ddaef0764736f6c634300080a00330000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102255760003560e01c80638d7024e51161012a578063d3b9fbe4116100bd578063e584b6541161008c578063e985e9c511610071578063e985e9c514610634578063ea3446bf14610657578063f5e293291461066a57600080fd5b8063e584b6541461060e578063e77366f81461062157600080fd5b8063d3b9fbe4146105de578063d8dfcea0146105e6578063da10610c146105ee578063dbe65edc1461060657600080fd5b8063abcd7830116100f9578063abcd78301461055b578063bd85b03914610598578063c7bd6586146105b8578063c9939f5e146105cb57600080fd5b80638d7024e5146104d257806395d89b411461050d578063a1af5b9a1461052c578063a41a01fb1461053457600080fd5b806347973bff116101bd578063551828941161018c5780637ca0de30116101715780637ca0de301461041c57806388cc58e41461046e5780638940a16a1461049457600080fd5b806355182894146103c45780636653851a1461040957600080fd5b806347973bff1461036b5780634c7cffbd1461037e5780634e1273f41461039157806353c059a0146103b157600080fd5b80630abe9688116101f95780630abe9688146102e357806317f11ecc1461031957806317fad7fc14610334578063383d15c51461034957600080fd5b8062fdd58e1461022a57806305e8746d1461025057806306fdde03146102795780630902f1ac146102bb575b600080fd5b61023d610238366004614e44565b61067d565b6040519081526020015b60405180910390f35b3660011981013560f01c90033560601c5b6040516001600160a01b039091168152602001610247565b60408051808201909152601481527f4c697175696469747920426f6f6b20546f6b656e00000000000000000000000060208201525b6040516102479190614e9c565b6102c36106ab565b604080516001600160801b03938416815292909116602083015201610247565b6102c36102f1366004614ee2565b62ffffff166000908152600760205260409020546001600160801b0381169160809190911c90565b6103216106e5565b60405161ffff9091168152602001610247565b610347610342366004614f42565b610706565b005b61035c610357366004614fd7565b61075f565b6040516102479392919061507a565b6103476103793660046150ab565b610a0b565b61023d61038c366004614ee2565b610aaa565b6103a461039f366004615142565b610ada565b60405161024791906151ae565b61023d6103bf3660046151cf565b610bc6565b6103cc610eed565b6040805160ff909616865261ffff9485166020870152939092169284019290925264ffffffffff918216606084015216608082015260a001610247565b610347610417366004615208565b610f94565b610424610ff8565b6040805161ffff9889168152968816602088015294871694860194909452918516606085015262ffffff9081166080850152931660a08301529190911660c082015260e001610247565b7f0000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e610261565b6104a76104a236600461528e565b611060565b6040805167ffffffffffffffff94851681529284166020840152921691810191909152606001610247565b6104da611156565b6040805162ffffff95861681529385166020850152919093169082015264ffffffffff9091166060820152608001610247565b60408051808201909152600381526213109560ea1b60208201526102ae565b61023d611196565b6105476105423660046152b5565b611362565b60405162ffffff9091168152602001610247565b61056e6105693660046152e1565b611375565b604080516001600160801b0394851681529284166020840152921691810191909152606001610247565b61023d6105a636600461531b565b60009081526001602052604090205490565b6103476105c6366004615334565b611516565b6103a46105d9366004614f42565b61158a565b6103476118a5565b6102c361195b565b3660011981013560f01c90036014013560601c610261565b610547611976565b61034761061c36600461534f565b611983565b61056e61062f3660046152e1565b611992565b61064761064236600461536d565b611ad3565b6040519015158152602001610247565b61034761066536600461538b565b611adf565b61054761067836600461531b565b611e3f565b6001600160a01b0382166000908152602081815260408083208484529091529020545b92915050565b905090565b6000806106dc6106c8600654600554611e6a90919063ffffffff16565b6001600160801b0381169160809190911c90565b90939092509050565b60006106a660003660011981013560f090811c90910360280135901c905090565b85336107128282611ea4565b6107475760405163548f773d60e01b81526001600160a01b038084166004830152821660248201526044015b60405180910390fd5b610755888888888888611ef0565b5050505050505050565b600080606060016003541461078757604051635db5c7cd60e11b815260040160405180910390fd5b6002600355866001600160a01b03811615806107ab57506001600160a01b03811630145b156107c9576040516345c210e760e11b815260040160405180910390fd5b856107e75760405163296db0d960e01b815260040160405180910390fd5b600060405180606001604052808989905067ffffffffffffffff81111561081057610810615414565b604051908082528060200260200182016040528015610839578160200160208202803683370190505b5081526020018867ffffffffffffffff81111561085857610858615414565b604051908082528060200260200182016040528015610881578160200160208202803683370190505b5081526020018867ffffffffffffffff8111156108a0576108a0615414565b6040519080825280602002602001820160405280156108c9578160200160208202803683370190505b5090526005549091506108f5813660011981013560f01c90038035606090811c9160140135901c61209c565b95506109048989888d866120dc565b945061091a6109138787611e6a565b8290612211565b600555841561094757610947853660011981013560f01c90038035606090811c9160140135901c8a61224b565b81604001519350896001600160a01b031660006001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8560000151886040516109a292919061542a565b60405180910390a4815160208301516040516001600160a01b038d169233927f87f1f9dcf5e8089a3e00811b6a008d8f30293a3da878cb1fe8c90ca376402f8a926109ee92919061542a565b60405180910390a350505060016003819055509450945094915050565b336001600160a01b037f0000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e1614610a5457604051632486085360e01b815260040160405180910390fd5b6004548015610a76576040516359e270a560e01b815260040160405180910390fd5b610a7e6122b1565b610a9f610a93610a8e83856122bd565b61230c565b8a8a8a8a8a8a8a612360565b505050505050505050565b60006106a0610ace60003660011981013560f090811c90910360280135901c905090565b62ffffff8416906124e7565b60608382808214610afe576040516340311ffd60e11b815260040160405180910390fd5b8567ffffffffffffffff811115610b1757610b17615414565b604051908082528060200260200182016040528015610b40578160200160208202803683370190505b50925060005b86811015610bbb57610b96888883818110610b6357610b6361544f565b9050602002016020810190610b789190615465565b878784818110610b8a57610b8a61544f565b9050602002013561067d565b848281518110610ba857610ba861544f565b6020908102919091010152600101610b46565b505050949350505050565b6000600160035414610beb57604051635db5c7cd60e11b815260040160405180910390fd5b6002600355600554600654600085610c1f57610c1a3660011981013560f01c90036014013560601c849061251e565b610c39565b610c393660011981013560f01c90033560601c8490612553565b905080610c5957604051637df801c760e01b815260040160405180910390fd5b610c638382612211565b6004549093506000610c8a60003660011981013560f090811c90910360280135901c905090565b90506000610c9783612582565b9050610ca28361258e565b92505b62ffffff8116600090815260076020526040902054610cc5818b15612603565b610df757610cd38483612632565b93506000806000610cf587878f888c896126ba9095949392919063ffffffff16565b919450925090508215610df357610d0c8884611e6a565b9750610d188b83612211565b9a506000610d33610d2889612882565b839061ffff16612892565b90508015610d5457610d458a82612211565b9950610d518482611e6a565b93505b610d6883610d628787612211565b90611e6a565b62ffffff87166000908152600760205260409020556001600160a01b038d16337fad7d6f97abf51ce18e17a38f4d70e975be9c0708474987bb3e26ad21bd93ca70888787610db58e612905565b6040805162ffffff9586168152602081019490945283019190915290911660608201526080810186905260a0810185905260c00160405180910390a3505b5050505b84610e025750610e4c565b6000610e0e8b84612916565b905062ffffff81161580610e26575062ffffff818116145b15610e4457604051631a6d7fb160e31b815260040160405180910390fd5b915050610ca5565b86610e6a5760405163439df85d60e11b815260040160405180910390fd5b610e748688611e6a565b6005556006859055610e88600b8483612938565b9250610e9483826122bd565b6004558815610ec057610ebb3660011981013560f01c90036014013560601c88908a612a48565b610edb565b610edb3660011981013560f01c90033560601c88908a612a88565b50506001600355509295945050505050565b60045460789060009081908190819081610f0682612a99565b905061ffff811615610f8b576000610f1f600b83612aa9565b96509050610f2c81612b07565b9650610f3781612b13565b945064ffffffffff8516610f4a57600095505b61ffff861615610f8957610f7b8661ffff168361ffff1681610f6e57610f6e615482565b600b916001910601612b34565b9050610f8681612b13565b93505b505b50509091929394565b336001600160a01b037f0000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e1614610fdd57604051632486085360e01b815260040160405180910390fd5b610fef60045488888888888888612360565b50505050505050565b600080600080600080600080600454905061101281612b07565b975061101d81612b7d565b965061102881612b8d565b955061103381612b9d565b945061103e81612bad565b935061104981612882565b925061105481612bbe565b91505090919293949596565b6000806000806004549050600061107682612a99565b905061ffff8116158061108f5750428664ffffffffff16115b156110a6576000806000945094509450505061114f565b60006110b4600b8389612bcf565b91985096509450905064ffffffffff808816908216101561114b576110e26110db84612582565b8490612cf2565b5060006110ef82896154ae565b90508064ffffffffff1661110285612582565b62ffffff1661111191906154d4565b61111b9088615504565b96508064ffffffffff1661112e85612905565b62ffffff1661113d91906154d4565b6111479087615504565b9550505b5050505b9193909250565b6000806000806000600454905061116c81612905565b945061117781612d09565b935061118281612d1a565b925061118d81612d2b565b91505090919293565b60006001600354146111bb57604051635db5c7cd60e11b815260040160405180910390fd5b60026003819055507f0000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e6001600160a01b0316634ccb20c06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611221573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112459190615530565b6001600160a01b0316336001600160a01b03161461127657604051630f8937c560e21b815260040160405180910390fd5b6006546001600160801b038116608082901c60006112d18261129957600061129c565b60015b60ff166000856001600160801b0316116112b75760006112ba565b60015b60ff169060801b6001600160801b03919091161790565b90506112dd8482611e6a565b945084156113545760068190556005546112f79086611e6a565b60055561131e853660011981013560f01c90038035606090811c9160140135901c3361224b565b60405185815233907f3f41a5ddc53701cc7db577ade4f1fca9838a8ec0b5ea50b9f0f5d17bc4554e329060200160405180910390a25b505060016003555090919050565b600061136e8383612916565b9392505050565b600454600090839082908161139f60003660011981013560f090811c90910360280135901c905090565b905060006113ac83612582565b90506113b78361258e565b92505b62ffffff81166000908152600760205260408120546113da908915612d3e565b90506001600160801b038116156114c05760006113fc62ffffff8416856124e7565b90506000876001600160801b0316836001600160801b03161161141f5782611421565b875b905061142d8685612632565b955060008a6114505761144b6001600160801b038316846080612d5e565b611465565b6114656001600160801b038316608085612d94565b905060006114738888612dca565b9050600061148a6001600160801b03841683612de9565b9050611496818461554d565b6114a0908d61554d565b9b506114ac848c61556f565b9a506114b8818b61554d565b995050505050505b6001600160801b0386166114d4575061150c565b60006114e08984612916565b905062ffffff811615806114f8575062ffffff818116145b1561150457505061150c565b9150506113ba565b5050509250925092565b600454600061152482612a99565b905061ffff81166115405750600161153c8282612e5b565b6004555b61154c600b8285612eaa565b60405161ffff8416815233907fc975541e72d695746a43ba65745d79963a23082637c8f4609354d9bcf70194d69060200160405180910390a2505050565b60606001600354146115af57604051635db5c7cd60e11b815260040160405180910390fd5b600260035586336115c08282611ea4565b6115f05760405163548f773d60e01b81526001600160a01b0380841660048301528216602482015260440161073e565b8515806115fd5750858414155b1561161b57604051633ab7870760e01b815260040160405180910390fd5b8567ffffffffffffffff81111561163457611634615414565b60405190808252806020026020018201604052801561165d578160200160208202803683370190505b5092506000805b878110156117b357600061168f8a8a848181106116835761168361544f565b90506020020135612f93565b905060008888848181106116a5576116a561544f565b90506020020135905080600014156116d757604051636996a92560e01b815262ffffff8316600482015260240161073e565b62ffffff8216600090815260076020908152604080832054600190925282205490915061170a8f8562ffffff1685612fbe565b600061171783858461303f565b90508061173e5760405163fd44792960e01b815262ffffff8616600482015260240161073e565b6117488382611e6a565b92508382141561175f5761175d6008866130b7565b505b62ffffff85166000908152600760205260409020839055895181908b908890811061178c5761178c61544f565b60209081029190910101526117a18782612211565b96508560010195505050505050611664565b506005546117c19082611e6a565b6005556117e8813660011981013560f01c90038035606090811c9160140135901c8c61224b565b60006001600160a01b03168a6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8b8b8b8b60405161183c94939291906155de565b60405180910390a4886001600160a01b0316336001600160a01b03167fa32e146844d6144a22e94c586715a1317d58a8aa3581ec33d040113ddcb243508a8a8860405161188b93929190615605565b60405180910390a350506001600355509695505050505050565b336001600160a01b037f0000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e16146118ee57604051632486085360e01b815260040160405180910390fd5b6004546119026118fd8261230c565b613152565b600455337f282afaeeae84c1d85ad1424a3aa2ddbedaeefca3b1e53d889d15265fe44db7fc61193083612d1a565b61193984612d09565b6040805162ffffff93841681529290911660208301520160405180910390a250565b60065460009081906001600160801b0381169060801c6106dc565b60006106a6600454612582565b61198e338383613187565b5050565b60008080806119aa6001600160801b03871686613270565b60045490915060006119d160003660011981013560f090811c90910360280135901c905090565b905060006119de83612582565b90506119e98361258e565b92505b62ffffff8116600090815260076020526040902054611a0c818a15612603565b611a7957611a1a8483612632565b935060008080611a2e8488888f898d6126ba565b919450925090508215611a7557611a458884611e6a565b9750611a52828d15612d3e565b611a5c908b61554d565b9950611a68818d612d3e565b611a72908a61554d565b98505b5050505b84611a845750611abc565b6000611a908a84612916565b905062ffffff81161580611aa8575062ffffff818116145b15611ab4575050611abc565b9150506119ec565b611ac68489612d3e565b9650505050509250925092565b600061136e8383611ea4565b600160035414611b0257604051635db5c7cd60e11b815260040160405180910390fd5b600260035582611b255760405163727ab02160e11b815260040160405180910390fd5b6005546004546000611b3686613281565b9050611b5c863660011981013560f01c90038035606090811c9160140135901c8a61224b565b6000806001600160a01b038916631faa6b8760e01b333660011981013560f01c90033560601c3660011981013560f01c90036014013560601c8c888d8d604051602401611baf979695949392919061562b565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c1a919061568f565b6000604051808303816000865af19150503d8060008114611c57576040519150601f19603f3d011682016040523d82523d6000602084013e611c5c565b606091505b5091509150811580611c7057508051602014155b80611cae57507fab5c473bce5960a8292e9c8db82f9272504caab4e9ef09553fc112f82b62a3c281806020019051810190611cab91906156ab565b14155b15611ccc57604051634a57011360e01b815260040160405180910390fd5b6000611cf1813660011981013560f01c90038035606090811c9160140135901c61209c565b9050611d07611d008786612211565b8290613389565b15611d2557604051630e3c717160e11b815260040160405180910390fd5b611d2f8187611e6a565b93506000611d3c86612582565b90506000611d5b8262ffffff1660009081526001602052604090205490565b15611d7c57611d77611d6c88612882565b879061ffff16612892565b611d7e565b855b6005849055600654909150611d939082612211565b600655611dbe611da38783611e6a565b62ffffff841660009081526007602052604090205490612211565b62ffffff83166000818152600760209081526040918290209390935580519182529181018d9052908101879052606081018290526001600160a01b038d169033907fd126bd9d94daca8e55ffd8283fac05394aec8326c6b1639e1e8a445fbe8bbc7d9060800160405180910390a35050600160035550505050505050505050565b60006106a0611e6360003660011981013560f090811c90910360280135901c905090565b83906133cd565b80820382811180611e8657506001600160801b03808416908216115b156106a05760405163e599af5560e01b815260040160405180910390fd5b6000816001600160a01b0316836001600160a01b0316148061136e5750506001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b8281808214611f12576040516340311ffd60e11b815260040160405180910390fd5b866001600160a01b0381161580611f3157506001600160a01b03811630145b15611f4f576040516345c210e760e11b815260040160405180910390fd5b6001600160a01b03808a16600090815260208190526040808220928b1682528120905b888110156120335760008a8a83818110611f8e57611f8e61544f565b9050602002013590506000898984818110611fab57611fab61544f565b600085815260208981526040909120549102929092013592505081811015612008578e83836040516358b4690f60e01b815260040161073e939291906001600160a01b039390931683526020830191909152604082015260600190565b6000928352602086815260408085209284900390925585905290912080549091019055600101611f72565b50896001600160a01b03168b6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8c8c8c8c60405161208794939291906155de565b60405180910390a45050505050505050505050565b60006120d484610d626120ae85613412565b6120b787613412565b6001600160801b03169060801b6001600160801b03919091161790565b949350505050565b6000806120fe60003660011981013560f090811c90910360280135901c905090565b600454909150600061210f82612582565b905086935060005b888110156122045760008061214d8a8d8d868181106121385761213861544f565b9050602002013561348190919063ffffffff16565b915091506000806000612163898886888c6134ca565b919450925090506121748a83611e6a565b99508362ffffff168b6000015187815181106121925761219261544f565b602002602001018181525050808b6020015187815181106121b5576121b561544f565b602002602001018181525050828b6040015187815181106121d8576121d861544f565b6020026020010181815250506121f48c8562ffffff16856136c7565b8560010195505050505050612117565b5050505095945050505050565b8181018281108061222d57506001600160801b03808416908216105b156106a0576040516316692de960e01b815260040160405180910390fd5b6001600160801b038416608085901c811561227d5761227d6001600160a01b038616846001600160801b038516613714565b6001600160801b038116156122a9576122a96001600160a01b038516846001600160801b038416613714565b505050505050565b6122bb6001600355565b565b60007cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83167fffffff000000000000000000000000000000000000000000000000000000000060e884901b161761136e565b60008061231883612582565b90507fffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffff831675ffffff00000000000000000000000000000000000000609883901b161761136e565b61ffff8716158015612374575061ffff8616155b8015612382575061ffff8516155b8015612390575061ffff8416155b801561239f575062ffffff8316155b80156123ad575061ffff8216155b80156123bc575062ffffff8116155b156123da576040516331375e0360e11b815260040160405180910390fd5b6123ea88888888888888886137ca565b9750600061240d60003660011981013560f090811c90910360280135901c905090565b9050600061241b8a846138ca565b905060006124298284613928565b6124338385613968565b61243d91906156c4565b905067016345785d8a0000811115612468576040516372db9c0b60e11b815260040160405180910390fd5b50505060048890556040805161ffff8981168252888116602083015287811682840152868116606083015262ffffff868116608084015290851660a0830152831660c0820152905133917fd09e5ddc721ff14c5c1e66a305cbba1fd70b82c5232bc391aad6f55e62e4b046919081900360e00190a25050505050505050565b600061271061ffff60801b608084901b1604600160801b0162ffffff8416627fffff1901612515828261398c565b95945050505050565b60008061252b8460801c90565b90506120d48161253a85613412565b612544919061556f565b6001600160801b031660801b90565b60006001600160801b0383166120d48161256c85613412565b612576919061556f565b6001600160801b031690565b600060e882901c6106a0565b60008061259a83612d2b565b6125ab9064ffffffffff16426156dc565b90506125b683612b7d565b61ffff1681106125fa576125c98361230c565b92506125d483612b8d565b61ffff1681106125ee576125e9836000613be5565b6125f7565b6125f783613152565b92505b61136e83613c53565b600081612623576126148360801c90565b6001600160801b03161561136e565b50506001600160801b03161590565b60008061263e84612d1a565b62ffffff169050600080828562ffffff1611612661578462ffffff16830361266a565b828562ffffff16035b9150612710820261267a87612d09565b62ffffff16019050600061268d87612bbe565b62ffffff1690508082116126a157816126a3565b805b91506126af87836138ca565b979650505050505050565b60008080806126ce62ffffff8716896124e7565b905060006126dd8b8915612d3e565b9050600088612708576127036126fe6001600160801b038416856080612d5e565b613cac565b612720565b6127206126fe6001600160801b038416608086612d94565b9050600061272e8c8c612dca565b905060006127456001600160801b03841683612de9565b9050612751818461554d565b9250600061275f8a8d612d3e565b9050600080856001600160801b0316836001600160801b03161061278a575084915082905085612800565b61279d6001600160801b03841686613cd6565b915060006127ab838561556f565b6001600160801b031690508e6127cf576127ca6126fe8260808c613d36565b6127de565b6127de6126fe828b6080613d60565b9150876001600160801b0316826001600160801b031611156127fe578791505b505b8d61283a57612818836001600160801b031660801b90565b6001600160801b038216612835846001600160801b031660801b90565b612862565b6001600160801b038316612857826001600160801b031660801b90565b6001600160801b0384165b809b50819c50829d50505050505050505050505096509650969350505050565b60006106a082604e1c613fff1690565b60006001600160801b0382166128aa575060006106a0565b6127106001600160801b0383168110156128d757604051631514a94960e21b815260040160405180910390fd5b6001600160801b03848116840282900490608086811c86028490049183169082901b175b9695505050505050565b60006106a08260701c620fffff1690565b60008261292d57612928600883613dce565b61136e565b61136e600883613f1a565b60008061294484612a99565b905061ffff8116612958578391505061136e565b60006129648683612b34565b905060006129718261404a565b9050600061297e83614056565b61298b9060ff16836156f3565b90508064ffffffffff1661299e42614065565b64ffffffffff161115612a3c5760008060006129db8464ffffffffff1642038a6129c78d612905565b6129d18e8e61408d565b8a939291906140bd565b92509250925060006129ec87612b07565b905064ffffffffff861642036078811115612a1c575096879006600101964295506000612a198c8a612e5b565b9b505b612a2a82868686858c614122565b97505050505050612a3c8885856141c6565b50949695505050505050565b6000612a548460801c90565b90506001600160801b03811615612a8257612a826001600160a01b038416836001600160801b038416613714565b50505050565b60006001600160801b038416612a54565b60006106a08260d81c61ffff1690565b600080612ab68484612b34565b9150612ac182612b07565b90508061ffff168361ffff1614612b0057612ae4612adf8583612b34565b612b07565b90508061ffff168361ffff1611612afb5780612afd565b825b90505b9250929050565b600061ffff82166106a0565b6000612b1e82614056565b60ff16612b2a8361404a565b6106a091906156f3565b60008161ffff8116612b595760405163944e113960e01b815260040160405180910390fd5b8361ffff60001985018116908110612b7357612b7361544f565b0154949350505050565b60006106a08260101c610fff1690565b60006106a082601c1c610fff1690565b60006106a08260281c613fff1690565b60006106a08260361c62ffffff1690565b60006106a082605c1c620fffff1690565b600080600080600080612be28989612aa9565b909250905064ffffffffff8716612c198a612bfd848c615713565b61ffff1661ffff8110612c1257612c1261544f565b0154612b13565b64ffffffffff161115612c3f57604051631c4e7f9560e21b815260040160405180910390fd5b612c4882612b13565b95508664ffffffffff168664ffffffffff1611612c8a5785612c698361420c565b612c7284614222565b612c7b85614238565b95509550955095505050612ce9565b869550600080612c9c8b8b8b8661424e565b91509150600089612cac83612b13565b612cb691906154ae565b90506000612cc384612b13565b612ccd908c6154ae565b9050612cdb8484848461439d565b919a50985096505050505050505b93509350935093565b6000612cfd8361258e565b925061136e8383612632565b60006106a08260841c620fffff1690565b60006106a08260981c62ffffff1690565b60006106a08260b01c64ffffffffff1690565b600081612d4f576129288360801c90565b6001600160801b03831661136e565b6000612d6b848484613d60565b9050600160ff83161b80612d8157612d81615482565b8385091561136e576120d46001826156c4565b6000612da1848484613d36565b90508180612db157612db1615482565b600160ff85161b85091561136e576120d46001826156c4565b600061136e612dd98484613928565b612de38585613968565b01613cac565b60008167016345785d8a0000816001600160801b03161115612e1e57604051638630bc6b60e01b815260040160405180910390fd5b6001600160801b03808416670de0b6b3a76400008190039182916000199188160282010181612e4f57612e4f615482565b049250505b5092915050565b60007fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff83167cffff00000000000000000000000000000000000000000000000000000060d884901b161761136e565b6000612eb68484612b34565b90506000612ec382612b07565b90508261ffff168161ffff1610612eed57604051632c57574160e21b815260040160405180910390fd5b60008461ffff168261ffff1614612f1e5761ffff821615612f1757612f128683612b34565b612f20565b6000612f20565b825b90506000612f2d82612b07565b61ffff169050808661ffff1611612f445780612f4a565b8561ffff165b905061ffff83165b8561ffff16811015612f7c5781888261ffff8110612f7257612f7261544f565b0155600101612f52565b50610fef878761ffff8681168818908916176141c6565b8062ffffff81168114612fb957604051639b63641560e01b815260040160405180910390fd5b919050565b6001600160a01b03831660009081526020818152604080832085845291829052909120548281101561301b57604051625b387760e31b81526001600160a01b0386166004820152602481018590526044810184905260640161073e565b60009384526001602090815260408086208054869003905592905292209103905550565b60006001600160801b038416608085901c828083156130725761306f6126fe886001600160801b038716896144fb565b91505b6001600160801b0383161561309b576130986126fe886001600160801b038616896144fb565b90505b608081901b6001600160801b0383161798975050505050505050565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b19811680821461314657600083815260028701602052604090208190558061313a57600883901c600081815260018089016020526040909120805460ff87169290921b1982169055925082613138578654600160ff83161b191687555b505b600193505050506106a0565b50600095945050505050565b60008061315e83612905565b62ffffff169050600061317084612b9d565b61ffff169050612710828202046125158582613be5565b826001600160a01b03811615806131a657506001600160a01b03811630145b156131c4576040516345c210e760e11b815260040160405180910390fd5b826001600160a01b0316846001600160a01b031614156132025760405163782ee70760e01b81526001600160a01b038516600482015260240161073e565b6001600160a01b03848116600081815260026020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b600081612d4f576129288360801b90565b6000807f0000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e6001600160a01b031663fd90c2be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061330691906156ab565b90506001600160801b0383811690608085901c90670de0b6b3a763ffff9061333f90670de0b6b3a7640000908616850283015b04613cac565b9250613371670de0b6b3a764000082866001600160801b0316856001600160801b031602018161333957613339615482565b915050608081901b6001600160801b03831617612515565b60006001600160801b0383811690608085811c9185169085901c818410806126af5750806001600160801b0316836001600160801b03161094505050505092915050565b600061271061ffff60801b608084901b1604600160801b01816133ef8261451b565b6133f88661451b565b6134029190615734565b9050612515816280000001612f93565b6040516370a0823160e01b81523060048201526000906106a0906001600160a01b038416906370a0823190602401602060405180830381865afa15801561345d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126fe91906156ab565b600080600080600061349287614626565b670de0b6b3a764000060808a811c90930281900490921b6001600160801b03998a169093029190910490971617979596505050505050565b62ffffff831660008181526007602052604081205490918291829182906134f1908b6124e7565b905060006135108962ffffff1660009081526001602052604090205490565b905061351e838984846146c9565b909650945084935062ffffff898116908b16141561364857613540878a612cf2565b9650600061355284898e89868c6147ef565b905080156136425760006135708461356a8985611e6a565b906148e1565b9050600061357e86866148e1565b905061358b8285836144fb565b985060006135a661359b8c612882565b859061ffff16612892565b905080156135cc576135b88882611e6a565b6006549098506135c89082612211565b6006555b6135d8600b8c8f612938565b9a508a600481905550336001600160a01b03167f3f0b46725027bb418b2005f4683538eccdbcdf1de2b8649a29dbd9c507d16ff48e86846040516136369392919062ffffff9390931683526020830191909152604082015260600190565b60405180910390a25050505b50613653565b613653858b8b6148fd565b85158061365e575083155b1561368357604051634c98d35760e11b815262ffffff8a16600482015260240161073e565b806136955761369360088a614968565b505b61369f8385612211565b62ffffff90991660009081526007602052604090209890985550929891975095509350505050565b600082815260016020526040812080548392906136e59084906156c4565b90915550506001600160a01b039092166000908152602081815260408083209383529290522080549091019055565b604080516001600160a01b03848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b17905290600090613781908616836149f2565b9050600081511180156137a55750808060200190518101906137a39190615762565b155b156137c35760405163197138bd60e11b815260040160405180910390fd5b5050505050565b60008561ffff168761ffff1611806137e75750610fff8661ffff16115b806137f757506127108561ffff16115b8061380757506109c48361ffff16115b806138195750620fffff8262ffffff16115b1561383757604051631c07203f60e01b815260040160405180910390fd5b5060109590951b630fff00001661ffff9690961695909517601c9390931b64fff0000000169290921760289190911b663fff0000000000161760369190911b693fffffc00000000000001617604e9290921b6b0fffc00000000000000000001691909117605c9190911b6dfffff0000000000000000000000016176dffffffffffffffffffffffffffff19919091161790565b6000620fffff8262ffffff1611156138f557604051631c07203f60e01b815260040160405180910390fd5b700fffff0000000000000000000000000000198316700fffff0000000000000000000000000000607084901b161761136e565b60008061393484612bad565b62ffffff1690508015612e545760008361ffff1661395186612905565b62ffffff1602905060646063828002840201612e4f565b60008161ffff1661397884612b07565b61ffff16026402540be40002905092915050565b60008080836139a45750600160801b91506106a09050565b508260008112156139b6579015906000035b62100000811015613ba657600160801b9250846001600160801b038111156139e057911591600019045b60018216156139f15792830260801c925b800260801c6002821615613a075792830260801c925b800260801c6004821615613a1d5792830260801c925b800260801c6008821615613a335792830260801c925b800260801c6010821615613a495792830260801c925b800260801c6020821615613a5f5792830260801c925b800260801c6040821615613a755792830260801c925b8002608090811c90821615613a8c5792830260801c925b800260801c610100821615613aa35792830260801c925b800260801c610200821615613aba5792830260801c925b800260801c610400821615613ad15792830260801c925b800260801c610800821615613ae85792830260801c925b800260801c611000821615613aff5792830260801c925b800260801c612000821615613b165792830260801c925b800260801c614000821615613b2d5792830260801c925b800260801c618000821615613b445792830260801c925b800260801c62010000821615613b5c5792830260801c925b800260801c62020000821615613b745792830260801c925b800260801c62040000821615613b8c5792830260801c925b800260801c62080000821615613ba45792830260801c925b505b82613bce57604051631dba598d60e11b8152600481018690526024810185905260440161073e565b81613bd95782612515565b6125158360001961577f565b6000620fffff8262ffffff161115613c1057604051631c07203f60e01b815260040160405180910390fd5b7fffffffffffffffffffffffffff00000fffffffffffffffffffffffffffffffff831672fffff000000000000000000000000000000000608484901b161761136e565b600080613c5f42614065565b90507fffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffffff83167affffffffff0000000000000000000000000000000000000000000060b083901b161761136e565b806001600160801b0381168114612fb957604051632f45db3d60e21b815260040160405180910390fd5b60008167016345785d8a0000816001600160801b03161115613d0b57604051638630bc6b60e01b815260040160405180910390fd5b670de0b6b3a7640000670de0b6b3a763ffff6001600160801b03868116908616020104949350505050565b600060ff831684811b9061ffff6101008290031686901c906128fb9087906001901b868585614abd565b6000806000613d6f8686614b66565b9150915081600014613d85578360ff1682901c92505b8015613dc557600160ff85161b8110613db157604051638e471a8960e01b815260040160405180910390fd5b8360ff166101000361ffff1681901b830192505b50509392505050565b60008061ffff600884901c1660ff808516908114613e235760008281526002870160205260408120549350613e038483614b85565b90506000198114613e215760089290921b90911792506106a0915050565b505b5060ff80821690600883901c908214613e9d5760008181526001880160205260408120549450613e538584614b85565b90506000198114613e9b57600882901b8117600081815260028a01602052604090205495509350613e8385614b94565b60ff1660088560001c901b17955050505050506106a0565b505b60ff81811692508214613f0d57865493506000613eba8584614b85565b90506000198114613f0b5760008181526001890160205260409020549450905080613ee485614b94565b60ff16600883901b17600081815260028a01602052604090205495509350613e8385614b94565b505b5060009695505050505050565b60008061ffff600884901c1660ff84168015613f6d5760008281526002870160205260408120549350613f4d8483614c30565b90506000198114613f6b5760089290921b90911792506106a0915050565b505b5060ff8116600882901c8115613fcc5760008181526001880160205260408120549450613f9a8584614c30565b90506000198114613fca57600882901b8117600081815260028a01602052604090205495509350613e8385614c40565b505b60ff81169150811561403b57865493506000613fe88584614c30565b90506000198114614039576000818152600189016020526040902054945090508061401285614c40565b60ff16600883901b17600081815260028a01602052604090205495509350613e8385614c40565b505b5062ffffff9695505050505050565b600060d882901c6106a0565b60006106a08260d01c60ff1690565b8064ffffffffff81168114612fb9576040516350dbfc8760e11b815260040160405180910390fd5b60008061409984612582565b90508062ffffff168362ffffff16116140b4578281036120d4565b90910392915050565b64ffffffffff841662ffffff8481168202918482168102918416026140e18861420c565b6140eb9084615504565b92506140f688614222565b6141009083615504565b915061410b88614238565b6141159082615504565b9050955095509592505050565b60d082901b7aff000000000000000000000000000000000000000000000000000016609084901b79ffffffffffffffff00000000000000000000000000000000000016605086901b71ffffffffffffffff0000000000000000000016601088901b69ffffffffffffffff00001661ffff8a161717171760d882901b7fffffffffff0000000000000000000000000000000000000000000000000000001681176126af565b8161ffff81166141e95760405163944e113960e01b815260040160405180910390fd5b818461ffff600019860181169081106142045761420461544f565b015550505050565b60006106a08260101c67ffffffffffffffff1690565b60006106a08260501c67ffffffffffffffff1690565b60006106a08260901c67ffffffffffffffff1690565b600080808061425e600186615793565b61ffff16905060008060008961ffff1690505b83851161431c576000600161428686886156c4565b901c9050888183089a508b61ffff808d169081106142a6576142a661544f565b015493506142b384612b13565b92508964ffffffffff168364ffffffffff1611156142dd576142d66001826156dc565b9450614316565b8964ffffffffff168364ffffffffff161015614305576142fe8160016156c4565b9550614316565b838497509750505050505050614394565b50614271565b8164ffffffffff168964ffffffffff16101561436a5761ffff8a1661433f578799505b8a61ffff6000198c0181169081106143595761435961544f565b015483965096505050505050614394565b8760018b089950828b61ffff808d169081106143885761438861544f565b01549650965050505050505b94509492505050565b6000806000806143ac8861420c565b67ffffffffffffffff16905060006143c389614222565b67ffffffffffffffff16905060006143da8a614238565b67ffffffffffffffff16905064ffffffffff87166143fe57919450925090506144f1565b60006144098a61420c565b67ffffffffffffffff16905060006144208b614222565b67ffffffffffffffff16905060006144378c614238565b67ffffffffffffffff16905064ffffffffff8b1661445f57919750955093506144f192505050565b600061447564ffffffffff808d16908e166156c4565b9050808b64ffffffffff1685028d64ffffffffff168902018161449a5761449a615482565b049950808b64ffffffffff1684028d64ffffffffff16880201816144c0576144c0615482565b049850808b64ffffffffff1683028d64ffffffffff16870201816144e6576144e6615482565b049750505050505050505b9450945094915050565b600080600061450a8686614b66565b915091506128fb8686868585614abd565b6000816001141561452f5750607f19919050565b8161454d5760405163125946cf60e11b815260040160405180910390fd5b60019190911c9060006f80000000000000000000000000000000831061457557506001614590565b5060001982600160fe1b8161458c5761458c615482565b0492505b600061459f607f85901c614c40565b707f80000000000000000000000000000000607f82901b16935060ff16905083811c6f80000000000000000000000000000000811461461b576f400000000000000000000000000000005b600081131561461957908002607f1c90600160801b8210614611579384019360019190911c905b60011d6145ea565b505b50500260011b919050565b6000808061463e8460581c67ffffffffffffffff1690565b92506146548460181c67ffffffffffffffff1690565b91505062ffffff831672ffffffffffffffffffffffffffffffffffffff84118061468f5750670de0b6b3a76400008367ffffffffffffffff16115b806146ab5750670de0b6b3a76400008267ffffffffffffffff16115b1561114f57604051637752557f60e11b815260040160405180910390fd5b6000806001600160801b038516608086901c826146e7838389614cdb565b90508515806146f4575080155b15614706579350869250614394915050565b60006147128a896148e1565b90508061472757509350869250614394915050565b6147328288836144fb565b9550600061474187838a614d46565b9050808311156147e057600061475782856156dc565b9050600160801b811061479857608081901c8581116147765780614778565b855b905061478481876156dc565b9550614794608082901b836156dc565b9150505b8981106147cc5760006147ab8b8361577f565b90508681116147ba57806147bc565b865b90506147c881886156dc565b9650505b608085901b6001600160801b038716179a50505b50949997985050505050505050565b6000816147fe575060006128fb565b6001600160801b038416608085901c6000806148306106c86148208d8b612211565b8861482b818c6156c4565b61303f565b91509150836001600160801b0316826001600160801b0316111561489357600061487661485d8c8c612dca565b614867848761556f565b6001600160801b031690614d63565b905061488b816001600160801b031660801b90565b9550506148d3565b826001600160801b0316816001600160801b031611156148d35760006148c66148bc8c8c612dca565b614867858861556f565b6001600160801b03169550505b505050509695505050505050565b60006001600160801b038316608084901c612515828286614cdb565b8162ffffff168162ffffff161080156149195750608083901b15155b8061493e57508162ffffff168162ffffff1611801561493e57506001600160801b0383115b156149635760405163227bd87160e21b815262ffffff8216600482015260240161073e565b505050565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b811780821461314657600083815260028701602052604090208190558161313a57600883901c600081815260018089016020526040909120805460ff87169290921b82179055925082613138578654600160ff83161b17875550600193505050506106a0565b6060600080846001600160a01b031684604051614a0f919061568f565b6000604051808303816000865af19150503d8060008114614a4c576040519150601f19603f3d011682016040523d82523d6000602084013e614a51565b606091505b50915091508115614a96578051158015614a7357506001600160a01b0385163b155b15614a9157604051633b99915360e21b815260040160405180910390fd5b6120d4565b8051614ab5576040516373483fab60e11b815260040160405180910390fd5b805181602001fd5b600081614adb57838381614ad357614ad3615482565b049050612515565b838210614afb576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000806000198385098385029250828110838203039150509250929050565b600061136e8360018401614dcd565b60008160801b8015614ba857608091508092505b8260401b90508015614bbb579150604001815b8260201b90508015614bce579150602001815b8260101b90508015614be1579150601001815b8260081b90508015614bf4579150600801815b8260041b90508015614c07579150600401815b8260021b90508015614c1a579150600201815b508160011b15614c28576001015b60ff03919050565b600061136e836000198401614dfb565b60006001600160801b03821115614c595750608090811c905b67ffffffffffffffff821115614c7157604091821c91015b63ffffffff821115614c8557602091821c91015b61ffff821115614c9757601091821c91015b60ff821115614ca857600891821c91015b600f821115614cb957600491821c91015b6003821115614cca57600291821c91015b6001821115612fb957600101919050565b60008315614d16575080830281848281614cf757614cf7615482565b0414614d16576040516363f1e01f60e01b815260040160405180910390fd5b821561136e5760809290921b9182018281101561136e576040516363f1e01f60e01b815260040160405180910390fd5b6000614d538484846144fb565b90508180612d8157612d81615482565b60008167016345785d8a0000816001600160801b03161115614d9857604051638630bc6b60e01b815260040160405180910390fd5b6ec097ce7bc90715b34b9f1000000000806001600160801b03868116908616908102670de0b6b3a76400009190910102612e4f565b60ff81169190911c9060008215614df15781614de884614b94565b0160ff1661136e565b5060001992915050565b60ff8181031691821b916000908315614e215780614e1885614c40565b60ff16036120d4565b506000199392505050565b6001600160a01b0381168114614e4157600080fd5b50565b60008060408385031215614e5757600080fd5b8235614e6281614e2c565b946020939093013593505050565b60005b83811015614e8b578181015183820152602001614e73565b83811115612a825750506000910152565b6020815260008251806020840152614ebb816040850160208701614e70565b601f01601f19169190910160400192915050565b803562ffffff81168114612fb957600080fd5b600060208284031215614ef457600080fd5b61136e82614ecf565b60008083601f840112614f0f57600080fd5b50813567ffffffffffffffff811115614f2757600080fd5b6020830191508360208260051b8501011115612b0057600080fd5b60008060008060008060808789031215614f5b57600080fd5b8635614f6681614e2c565b95506020870135614f7681614e2c565b9450604087013567ffffffffffffffff80821115614f9357600080fd5b614f9f8a838b01614efd565b90965094506060890135915080821115614fb857600080fd5b50614fc589828a01614efd565b979a9699509497509295939492505050565b60008060008060608587031215614fed57600080fd5b8435614ff881614e2c565b9350602085013567ffffffffffffffff81111561501457600080fd5b61502087828801614efd565b909450925050604085013561503481614e2c565b939692955090935050565b600081518084526020808501945080840160005b8381101561506f57815187529582019590820190600101615053565b509495945050505050565b838152826020820152606060408201526000612515606083018461503f565b803561ffff81168114612fb957600080fd5b600080600080600080600080610100898b0312156150c857600080fd5b6150d189615099565b97506150df60208a01615099565b96506150ed60408a01615099565b95506150fb60608a01615099565b945061510960808a01614ecf565b935061511760a08a01615099565b925061512560c08a01614ecf565b915061513360e08a01614ecf565b90509295985092959890939650565b6000806000806040858703121561515857600080fd5b843567ffffffffffffffff8082111561517057600080fd5b61517c88838901614efd565b9096509450602087013591508082111561519557600080fd5b506151a287828801614efd565b95989497509550505050565b60208152600061136e602083018461503f565b8015158114614e4157600080fd5b600080604083850312156151e257600080fd5b82356151ed816151c1565b915060208301356151fd81614e2c565b809150509250929050565b600080600080600080600060e0888a03121561522357600080fd5b61522c88615099565b965061523a60208901615099565b955061524860408901615099565b945061525660608901615099565b935061526460808901614ecf565b925061527260a08901615099565b915061528060c08901614ecf565b905092959891949750929550565b6000602082840312156152a057600080fd5b813564ffffffffff8116811461136e57600080fd5b600080604083850312156152c857600080fd5b82356152d3816151c1565b9150612afd60208401614ecf565b600080604083850312156152f457600080fd5b82356001600160801b038116811461530b57600080fd5b915060208301356151fd816151c1565b60006020828403121561532d57600080fd5b5035919050565b60006020828403121561534657600080fd5b61136e82615099565b6000806040838503121561536257600080fd5b823561530b81614e2c565b6000806040838503121561538057600080fd5b82356151ed81614e2c565b600080600080606085870312156153a157600080fd5b84356153ac81614e2c565b935060208501359250604085013567ffffffffffffffff808211156153d057600080fd5b818701915087601f8301126153e457600080fd5b8135818111156153f357600080fd5b88602082850101111561540557600080fd5b95989497505060200194505050565b634e487b7160e01b600052604160045260246000fd5b60408152600061543d604083018561503f565b8281036020840152612515818561503f565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561547757600080fd5b813561136e81614e2c565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600064ffffffffff838116908316818110156154cc576154cc615498565b039392505050565b600067ffffffffffffffff808316818516818304811182151516156154fb576154fb615498565b02949350505050565b600067ffffffffffffffff80831681851680830382111561552757615527615498565b01949350505050565b60006020828403121561554257600080fd5b815161136e81614e2c565b60006001600160801b0380831681851680830382111561552757615527615498565b60006001600160801b03838116908316818110156154cc576154cc615498565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156155c157600080fd5b8260051b8083602087013760009401602001938452509192915050565b6040815260006155f260408301868861558f565b82810360208401526126af81858761558f565b60408152600061561960408301858761558f565b82810360208401526128fb818561503f565b60006001600160a01b03808a168352808916602084015280881660408401525085606083015284608083015260c060a08301528260c0830152828460e0840137600060e0848401015260e0601f19601f850116830101905098975050505050505050565b600082516156a1818460208701614e70565b9190910192915050565b6000602082840312156156bd57600080fd5b5051919050565b600082198211156156d7576156d7615498565b500190565b6000828210156156ee576156ee615498565b500390565b600064ffffffffff80831681851680830382111561552757615527615498565b600061ffff8084168061572857615728615482565b92169190910692915050565b60008261574357615743615482565b600160ff1b82146000198414161561575d5761575d615498565b500590565b60006020828403121561577457600080fd5b815161136e816151c1565b60008261578e5761578e615482565b500490565b600061ffff838116908316818110156154cc576154cc61549856fea26469706673582212205adcacc1d216d119797176c20bff4d077411de5b9bc12719a34da1f62ddaef0764736f6c634300080a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e
-----Decoded View---------------
Arg [0] : factory_ (address): 0x8e42f2F4101563bF679975178e880FD87d3eFd4e
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000008e42f2f4101563bf679975178e880fd87d3efd4e
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.